Ideas for mod files XML and a DTD

Started by Vendetta, November 12, 2005, 12:39:47 AM

Previous topic - Next topic

Vendetta

First: I switched to SMF a few deys ago (from phpBB) and I like it alot!

Of course, there are some functionalitys not provided yet, so I plan to write some mods. I already did one simple, and it looks like I understand the code quite alright.

So the problem is not the modifications themselves, but how to get this to work with the package manager. I've found the package SDK, but it has far less information then I expected. For some reason the http://mods.simplemachines.org/docs/ site is always offline (404) ...

So, is there a detailed description of how to use the different xml files? A list of all elements, their attributes and how they are expected to work? I will find my way through it, but this would be nice. In the meantime I will stick with the DTD's

However, I love the idea of writing mod in xml. But as far as I can see, there are some unused possibilitys in the status quo:

As a sibling to <file> there should be an element called <sql> for SQL operations. It should contain two elements called <test> and <query>.

A DB modification should look like this:

<sql>
  <test for="mysql_num_rows" to="0"><![CDATA[
    SHOW COLUMNS FROM tablename LIKE 'fieldname']]></test>
  <query><![CDATA[
    ALTER TABLE tablename ADD fieldname FIELD PROPERTYS]]></query>
</sql>


Second, there should be an universal attribute called "compatibility". Let me give you an example of a pseudo install.xml:

<modification compatibility="1.03 1.04 1.05 1.1RC1">
  <id></id>
  <version></version>

  <file name="filename" compatibility="all">
    <operation compatible="1.03">
      <!-- Operation compatible with 1.03 --></operation>
    <opation compatible="!1.1RC1">
      <!-- Operation compatible with all versions (as listed in <modification>) exept for 1.1RC1  --></operation>
    <operation>
      <!-- If no compatibility is set, for backwards compatibility "all" is persumed -->
      <search position="before" compatibility="1.03 1.04">
        <!-- Search for this only in 1.03 and 1.04 --></search>
      <search position="before" compatibility="else">
        <!-- Search for this in all but 1.03 and 1.04 --></search>
      <add compatibility="!1.1RC1">
        <!-- Add this in all but 1.1RC1 --></add>
      <add>
        <!-- As there is only one <add> allowed to be performed, 'compatibility="else"' is persumed --></add>
    </operation>
  </file>
</modification>

So special values would be "all" and "else". Looks good to me, what do you think?

And one more:
I would like a more sophisticated way of doing the actual insert commands. The search and add thing is ok, but I think it could be optimised:

1) remove the "position" attribute from "search" element
2) add elements "replace" and "remove" ("remove" is an empty element)
3) add an "position" attribute to the "add" element.
4) "position" should accept the special values "before" and "after" or a number. Positive numbers and 0 are counted from the start of the search string, negative from the end. "after" should be the default value.
5) add a "position" attribute to the "replace" and "remove" element.
6) "position" for "replace" and "remove" should have two space separated values, one for the start and one for the end of the string to be replaced. default value should be "before after". If only one value is given, the cutted string should be as long as the replacement string.
7) a "regexp" element as a substitution to 'search regexp="true"'

Example:

<!-- The result of all operations is "foo bar" -->
<operation>
  <search><![CDATA[foo]]></search>
  <add><![CDATA[ bar]]></add>
</operation>

<operation>
  <search><![CDATA[foo bur]]></search>
  <replace position="6"><![CDATA[a]]></replace>
</operation>

<operation>
  <search><![CDATA[foo bar foo]]></search>
  <remove position="7 after" />
</operation>

<operation>
  <regexp bracket="2"><![CDATA[/^foo (bar)?(foo)$/i]]></regexp>
  <remove />
</operation>


I'll finish after the next one:
You could add an element to define packages which are incompatible with this one. Lets call it "contraindication". Example:

<modification>
  <id></id>
  <version></version>
  <contraindication>some:mod_id</contraindication>
  <contraindication>another:mod_id</contraindication>
</modification>


Ok, thats it for now, hope to read from you.

Regards

Vendetta

p.s.: I hope I will finde time to do some of these things myself, but as time goes by ...

Vendetta

I know, that writing the DTD is not the main work -- but anyway, I've done it, so here it is:

<!ELEMENT modification (id, version, contraindication*, (file|sql)+)>
<!ATTLIST modification
  xmlns CDATA "http://www.simplemachines.org/xml/modification"
  xmlns:smf CDATA "http://www.simplemachines.org/"
  compatibility CDATA #REQUIRED
>

<!ELEMENT id (#PCDATA)>
<!ELEMENT version (#PCDATA)>
<!ELEMENT contraindication (#PCDATA)>
<!ATTLIST contraindication
  compatibility CDATA "all"
>


<!ELEMENT sql (test*,query+)>
<!ATTLIST sql
  compatibility CDATA "all"
>
  <!ELEMENT test (#PCDATA)>
  <!ATTLIST test
    for (num_rows|errno) #REQUIRED
    to CDATA #REQUIRED
    compatibility CDATA "all"
  >
  <!ELEMENT query (#PCDATA)>
  <!ATTLIST query
    compatibility CDATA "all"
  >

<!ELEMENT file (operation+)>
<!ATTLIST file
  name CDATA #REQUIRED
  error (ignore|fatal|skip) "fatal"
  compatibility CDATA "all"
>
  <!ELEMENT operation ((search|regexp)+, (add|replace|remove)+)>
  <!ATTLIST operation
    error (ignore|fatal|required) "fatal"
    compatibility CDATA "all"
  >
    <!ELEMENT search (#PCDATA)>
    <!ATTLIST search
      whitespace (exact|loose) "exact"
      compatibility CDATA "all"
    >
    <!ELEMENT regexp (#PCDATA)>
    <!ATTLIST regexp
      bracket CDATA "0"
      compatibility CDATA "all"
    >
    <!ELEMENT add (#PCDATA)>
    <!ATTLIST add
      position NMTOKEN "after"
      compatibility CDATA "all"
    >
    <!ELEMENT replace (#PCDATA)>
    <!ATTLIST replace
      position NMTOKENS "before after"
      compatibility CDATA "all"
    >
    <!ELEMENT remove EMPTY>
    <!ATTLIST remove
      position NMTOKENS "before after"
      compatibility CDATA "all"
    >

<!-- These are really common... -->
<!ENTITY nbsp " ">
<!ENTITY reg "®">
<!ENTITY copy "©">


Maybe someone has a tip for writing the parser? I haven't had a look at it yet...

Vendetta

Vendetta

Here is an example of a whole install.xml with some additional notes. You should NOT read this here (where is the syntax highlighting?) but in a XML editor.

To validate this, insert the DTD from my last posting at the top inside the DOCTYPE.

<?xml version="1.0"?>
<!DOCTYPE modification [
INSERT DTD
]>
<!--
Please note that I'm not quite sure about the DTD, it is the first I wrote.
Anyway, jedits xml plugin verifys this example -->

<!-- COMPATIBILITY:
A new universal attribute called "compatibility" to indicate that this part of
the tree is only for some versions of smf.
The compatibility in "modification" is a general compatibility. If your version
of smf is not listed here, you cannot install this mod.
While all compatibility attributes in other elements are optional and default to
"all", the attribute is required in the root element!
This is to ensure that no mods are installed without compatibility notes. -->
<modification compatibility="1.03 1.04 1.05 1.1RC1">
  <id>Vendetta:packageManagerXMLExample</id>
  <version>1.0</version>
 
  <!-- CONTRAINDICATION:
  List mod that are incomplatible with this mod. Having one of these mod installed
  will cancle the installation -->
  <contraindication>some:mod_id</contraindication>
  <contraindication>another:mod_id</contraindication>
 
  <!-- SQL:
  A new element called "sql" for database operations -->
  <sql>
    <!-- TEST:
    The "test" element is for a query that must be performed and be
    validated to let the actual query be queried ;)
    "for" should be the name of a php db-function. Here it should read
    mysql_num_rows, but I don't write the "mysql_" to be open for other
    DBs to be supported ...
    "to" is the result that performing the test with "for" should give.
   
    I should not be necessary to write the {$db_prefix} before the tablename,
    but this could be a hard one. -->
    <test for="num_rows" to="0"><![CDATA[
      SHOW COLUMNS FROM tablename LIKE 'fieldname']]></test>
    <!-- QUERY:
    This is the actual query to be performed. I added two different version
    for compatibility example -->
    <query compatibility="!1.1RC1"><![CDATA[
      ALTER TABLE tablename ADD fieldname FIELD PROPERTYS]]></query>
    <query><![CDATA[
      ALTER TABLE tablename ADD 11rc1_fieldname FIELD PROPERTYS]]></query>
  </sql>


  <!-- A detailed example of how compatibility should work -->
  <file name="filename" compatibility="all">
    <operation compatibility="1.03">
      <!-- Operation compatible with 1.03 -->
      <search></search>
      <add></add>
    </operation>
    <operation compatibility="!1.1RC1">
      <!-- Operation compatible with all versions (as listed in <modification>) exept for 1.1RC1  -->
      <search></search>
      <add></add>
    </operation>
    <operation>
      <!-- If no compatibility is set, for backwards compatibility "all" is persumed -->
      <search compatibility="1.03 1.04">
        <!-- Search for this only in 1.03 and 1.04 --></search>
      <search compatibility="else">
        <!-- Search for this in all but 1.03 and 1.04 --></search>
      <add compatibility="!1.1RC1">
        <!-- Add this in all but 1.1RC1 --></add>
      <add>
        <!-- As there is only one <add> allowed to be performed, 'compatibility="all"' is persumed --></add>
    </operation>
  </file>
  <!-- One word to the difference between "all" and "else":
  Generally, there is only one "search|regexp" and one "add|replace|remove" executed.
  So, if the parser executes the first e.g. "search", none of the following
  searches or regexp's will be executed.
  If the parser findes an "all", it will execute it without further question (if
  it hasn't already executed another one...)
  If the parser findes an "else", it will stop with an error if no preceding
  element of same type was found. Otherwise it will act like with "all".
 
  So mayby its unimportand to have the "else" value at all. But I think it comes
  handy for the modwriter to see the difference in the code. But anyway, not realy
  importand -->
 
 
  <!-- Some examples for operations. The result of all operations is "foo bar" -->
  <file name="somefile">
    <operation>
      <search><![CDATA[foo]]></search>
      <add><![CDATA[ bar]]></add>
    </operation>
   
    <operation>
      <search><![CDATA[bar foo]]></search>
      <replace><![CDATA[foo bar]]></replace>
    </operation>
   
    <operation>
      <search><![CDATA[foo bur]]></search>
      <replace position="6"><![CDATA[a]]></replace>
    </operation>
   
    <operation>
      <search><![CDATA[foo bar foo]]></search>
      <remove position="7 after" />
    </operation>
   
    <!-- "foo bar foo" or "foo foo" to "foo bar" or "foo" -->
    <operation>
      <regexp bracket="2"><![CDATA[/^foo (bar)?(foo)$/i]]></regexp>
      <remove />
    </operation>
  </file>
</modification>

Sheepy

Well... I agree that a package documentation is needed badly.  Even a list of tag and which does what would be really helpful.

sql operations would be nice.  Most mods need only a short query or two, if any.  And that means once updated my mod editor will be able to handle more complicated mods.

Vendetta

Hi Sheepy

I had a look at your mod editor, but due to a lack of time, I only read through it, haven't tryed. But it looks like a good work :)

I think, best would be to have the whole instructions in just one file. I don't know, is there any reason to have executable php code in a mod exept for database querys? I don't think so. Even if it were, you could use the <code> element for it (inline to be easy to edit?)

I don't think that there is a real reason to have the file/dir operations and the in-file operations separated. And even with install and uninstall, you could simply use xml elements.

If I feel like it, I will provide another example of a DTD with all elements in one file. Mayby I'm too tired today

Regards

Vendetta

Vendetta

Ok, I did it, see the code below, I tryed to be detailed anough in my comments to be understood ;)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE modification [
<!-- modification is the root element!
I deleted the compatibility attribute here, and added an compatible
element to info -->
<!ELEMENT modification (info,install,uninstall,update*)>
<!ATTLIST modification
  xmlns CDATA "http://www.simplemachines.org/xml/modification"
  xmlns:smf CDATA "http://www.simplemachines.org/"
>
  <!-- info contains all information about the modification -->
  <!ELEMENT info (id,version,author*,readme,compatible+,contraindication*)>

    <!ELEMENT id (#PCDATA)>
    <!ELEMENT version (#PCDATA)>
   
    <!-- author is new, here you can provide contact information for support.
    More than one author is posiible -->
    <!ELEMENT author (name,email,uri)>
      <!ELEMENT name (#PCDATA)>
      <!ELEMENT email (#PCDATA)>
      <!ELEMENT uri (#PCDATA)>
     
    <!-- element compatible, no version of smf will execute anything if it is
    not listed here -->
    <!ELEMENT compatible (#PCDATA|contraindication)*>
   
    <!-- contraindication may occure at two positions:
    child of <info> and child of <compatible>
    In the first case it indicates that the mod is not compatible with another
    mod.
    In the second case it indicates that the mod is not compatible with another
    mod in this particular version of smf -->
    <!ELEMENT contraindication (#PCDATA)>
   
  <!-- Well, what do you guess are these elements for :? -->
  <!ELEMENT install (readme?,(eval|create-dir|create-file|require-dir|require-file|move-dir|move-file|remove-dir|remove-file|sql|file)+)>
  <!ELEMENT uninstall (readme?,(eval|create-dir|create-file|require-dir|require-file|move-dir|move-file|remove-dir|remove-file|sql|file)*)>
  <!-- I made the reverse attribute a fixed one as boolean attributes normaly are -->
  <!ATTLIST uninstall
    reverse CDATA #FIXED "reverse"
  >
  <!ELEMENT update (readme?,(eval|create-dir|create-file|require-dir|require-file|move-dir|move-file|remove-dir|remove-file|sql|file)+)>
  <!ATTLIST update
    from CDATA #REQUIRED
  >
 
<!-- Now comming: All elements that may occure inside <install>, <uninstall> or <update> -->
 
    <!-- I modifyed the readme to match common practise like the HTML script tag.
    And the readmy is now required. Its not too hard to write some sentences -->
    <!ELEMENT readme (#PCDATA)>
    <!ATTLIST readme
      src CDATA #IMPLIED
      type CDATA "text/plain"
      compatibility CDATA "all"
    >
     
    <!-- I do the same with the eval element (aka code), but its still optional. -->
    <!ELEMENT eval (#PCDATA)>
    <!ATTLIST eval
      src CDATA #IMPLIED
      compatibility CDATA "all"
    >
   
<!-- filesystem operations, mainly addopted from the DTD in use, but made EMPTY -->
   
<!-- Create a new directory, named name and put it in destination. -->
<!ELEMENT create-dir EMPTY>
<!ATTLIST create-dir
        name CDATA #REQUIRED
        destination CDATA #REQUIRED
        compatibility CDATA "all"
      >

<!-- Create a blank file, name it name and put it in destination. -->
<!ELEMENT create-file EMPTY>
<!ATTLIST create-file
        name CDATA #REQUIRED
        destination CDATA #REQUIRED
        compatibility CDATA "all"
      >

<!-- Require a directory (and all files in it!) from inside the package. -->
<!ELEMENT require-dir EMPTY>
<!-- Put it in destination, name it name, get it from from. (or name if there's no from..) -->
<!ATTLIST require-dir
        from CDATA #IMPLIED
        name CDATA #REQUIRED
        destination CDATA #REQUIRED
        compatibility CDATA "all"
      >

<!-- Require a file from inside the package. -->
<!ELEMENT require-file EMPTY>
<!-- Stick it in destination, name it name, and get it from from. (or name if there's no from..) -->
<!ATTLIST require-file
        from CDATA #IMPLIED
        name CDATA #REQUIRED
        destination CDATA #REQUIRED
        compatibility CDATA "all"
      >

<!-- Move an entire directory, named name, to destination. -->
<!ELEMENT move-dir EMPTY>
<!-- Use from as the from name if it's specified. (allows for renaming.) -->
<!ATTLIST move-dir
        from CDATA #IMPLIED
        name CDATA #REQUIRED
        destination CDATA #REQUIRED
        compatibility CDATA "all"
      >

<!-- Move a file from one place to another. (name -> destination) -->
<!ELEMENT move-file EMPTY>
<!-- Use from as the from name if it's specified. (allows for renaming files.) -->
<!ATTLIST move-file
        from CDATA #IMPLIED
        name CDATA #REQUIRED
        destination CDATA #REQUIRED
        compatibility CDATA "all"
      >

<!-- Remove an entire directory (!!) named name. -->
<!ELEMENT remove-dir EMPTY>
<!ATTLIST remove-dir
        name CDATA #REQUIRED
        compatibility CDATA "all"
      >

<!-- Remove (delete) a file named name. -->
<!ELEMENT remove-file EMPTY>
<!ATTLIST remove-file
        name CDATA #REQUIRED
        compatibility CDATA "all"
      >
   
<!-- So we have finished the filesystem operation part, now comes the sql part -->
   
    <!-- I extended the sql element to make it more flexible.
    You can use the src attribute to execute a file of sql statements
    (but then you cannot test??) -->
    <!ELEMENT sql (test*,query*)>
    <!ATTLIST sql
      server (mysql) "mysql"
      version CDATA "all"
      src CDATA #IMPLIED
      compatibility CDATA "all"
    >
      <!ELEMENT test (#PCDATA)>
      <!ATTLIST test
        for (num_rows|errno) #REQUIRED
        to CDATA #REQUIRED
        compatibility CDATA "all"
      >
      <!ELEMENT query (#PCDATA)>
      <!ATTLIST query
        compatibility CDATA "all"
      >

<!-- We go on to the in-file operations -->
     
      <!-- The file element is not modified except for the compatibility attribute. -->
      <!ELEMENT file (operation+)>
      <!-- What do we do on failure?  To skip the file, use "skip", and to create a file, use "ignore". -->
      <!ATTLIST file
        name CDATA #REQUIRED
        error (ignore|fatal|skip) "fatal"
        compatibility CDATA "all"
      >
       
        <!-- for compatibility, there is more then one search... and add...
        element allowed, although only one is executed: The first that matches
        compatibility -->
        <!ELEMENT operation ((search|regexp)+, (add|replace|remove)*)>
        <!-- Can it fail?  Use "required" if it must fail. -->
        <!ATTLIST operation
          error (ignore|fatal|required) "fatal"
          compatibility CDATA "all"
        >
       
          <!-- search works different! I removed the position attribute, so
          it just searches and finds -->
          <!ELEMENT search (#PCDATA)>
          <!ATTLIST search
            whitespace (exact|loose) "exact"
            compatibility CDATA "all"
          >
         
          <!-- or use regexp: Enter a standard regular expression to search.
          use the bracket attribute to specify which part of the regexp should
          be the part to work with. This allows to make better use of the position
          attribute at add|replace|remove -->
          <!ELEMENT regexp (#PCDATA)>
          <!ATTLIST regexp
            bracket CDATA "0"
            compatibility CDATA "all"
          >
         
          <!-- add adds a string. You can add the string at any position within
          the found string, use "before" and "after" for start attribute to add
          it at the start or at the end position. Use numbers for other positions,
          starting with 0 (= before) or from the back with negative numbers -->
          <!ELEMENT add (#PCDATA)>
          <!ATTLIST add
            start NMTOKEN "after"
            compatibility CDATA "all"
          >
         
          <!-- replace cuts out a part of the found string and replaces it with
          the given string. Use start and stop as described at add. -->
          <!ELEMENT replace (#PCDATA)>
          <!ATTLIST replace
            start NMTOKEN "before"
            stop NMTOKEN "after"
            compatibility CDATA "all"
          >
         
          <!-- remove simply removes a part of the found string. Use start and
          stop as described at replace -->
          <!ELEMENT remove EMPTY>
          <!ATTLIST remove
            start NMTOKEN "before"
            stop NMTOKEN "after"
            compatibility CDATA "all"
          >

<!-- These are really common... -->
<!ENTITY nbsp " ">
<!ENTITY reg "®">
<!ENTITY copy "©">

<!-- Entitys to be used with regexp to get results -->
<!ENTITY reg0 "\0">
<!ENTITY reg1 "\1">
<!ENTITY reg2 "\2">
<!ENTITY reg3 "\3">
<!ENTITY reg4 "\4">
<!ENTITY reg5 "\5">
<!ENTITY reg6 "\6">
<!ENTITY reg7 "\7">
<!ENTITY reg8 "\8">
<!ENTITY reg9 "\9">
]>
<!--
Please note that I'm not quite sure about the DTD, it is the first I wrote.
Anyway, jedits xml plugin verifys this example -->
<!-- COMPATIBILITY:
A new universal attribute called "compatibility" to indicate that this part of
the tree is only for some versions of smf.
The compatibility in "modification" is a general compatibility. If your version
of smf is not listed here, you cannot install this mod.
While all compatibility attributes in other elements are optional and default to
"all", the attribute is required in the root element!
This is to ensure that no mods are installed without compatibility notes. -->
<modification>
  <info>
    <id>Vendetta:packageManagerXMLExample</id>
    <version>1.0a2</version>
    <author>
      <name>Vendetta</name>
      <email>[email protected]</email>
      <uri>http://www.server.org</uri>
    </author>
    <readme src="README.txt" type="text/plain">You can find the README at
      http://www.server.org/readme/README</readme>
    <compatible>1.03</compatible>
    <compatible>1.04</compatible>
    <compatible>1.05</compatible>
    <compatible>1.1RC1
      <contraindication>Author:ModName</contraindication>
    </compatible>
    <!-- CONTRAINDICATION:
    List mod that are incomplatible with this mod. Having one of these mod installed
    will cancle the installation -->
    <contraindication>some:mod_id</contraindication>
    <contraindication>another:mod_id</contraindication>
  </info>
 
  <install>
    <readme src="INSTALL.txt" type="text/plain">You can find the INSTALL at
      http://www.server.org/readme/INSTALL</readme>
    <create-file name="testfile" destination="sourcedir" />
   
    <!-- SQL:
    A new element called "sql" for database operations -->
    <sql server="mysql" version="4.01-4.15">
      <!-- TEST:
      The "test" element is for a query that must be performed and be
      validated to let the actual query be queried ;)
      "for" should be the name of a php db-function. Here it should read
      mysql_num_rows, but I don't write the "mysql_" to be open for other
      DBs to be supported ...
      "to" is the result that performing the test with "for" should give.
     
      I should not be necessary to write the {$db_prefix} before the tablename,
      but this could be a hard one. -->
      <test for="num_rows" to="0"><![CDATA[
        SHOW COLUMNS FROM tablename LIKE 'fieldname']]></test>
      <!-- QUERY:
      This is the actual query to be performed. I added two different version
      for compatibility example -->
      <query compatibility="!1.1RC1"><![CDATA[
        ALTER TABLE tablename ADD fieldname FIELD PROPERTYS]]></query>
      <query><![CDATA[
        ALTER TABLE tablename ADD 11rc1_fieldname FIELD PROPERTYS]]></query>
    </sql>
 
 
    <!-- A detailed example of how compatibility should work -->
    <file compatibility="all" name="filename">
      <operation compatibility="1.03">
        <!-- Operation compatible with 1.03 -->
        <search></search>
        <add></add>
      </operation>
      <operation compatibility="!1.1RC1">
        <!-- Operation compatible with all versions (as listed in <modification>) exept for 1.1RC1  -->
        <search></search>
        <add></add>
      </operation>
      <operation>
        <!-- If no compatibility is set, for backwards compatibility "all" is persumed -->
        <search compatibility="1.03 1.04">
          <!-- Search for this only in 1.03 and 1.04 --></search>
        <search compatibility="all">
          <!-- Search for this in all but 1.03 and 1.04 --></search>
        <add compatibility="!1.1RC1">
          <!-- Add this in all but 1.1RC1 --></add>
        <add>
          <!-- As there is only one <add> allowed to be performed, 'compatibility="all"' is persumed --></add>
      </operation>
    </file>
   
    <!-- Some examples for operations. The result of all operations is "foo bar" -->
    <file name="somefile">
      <operation>
        <search>foo</search>
        <add> bar</add>
      </operation>
     
      <operation>
        <search>bar foo</search>
        <replace>foo bar</replace>
      </operation>
     
      <operation>
        <search>foo bur</search>
        <replace start="-3" stop="-2">a</replace>
      </operation>
     
      <operation>
        <search>foo bar foo</search>
        <remove start="7" stop="after" />
      </operation>
     
      <operation>
        <regexp bracket="1"><![CDATA[/^((foo)+ (bar)+)$/i]]></regexp>
        <replace>&reg2; &reg3;</replace>
      </operation>
    </file>
  </install>
 
  <!-- The uninstall: If the reverse="reverse" is set, the install process is
  performed reverse. Inside there may be additional operations to be performed
  unreversed. -->
  <uninstall reverse="reverse">
 
    <!-- the eval element: formaly known as <code>.
    This is the usual way of including an executable php code:
    The actual code is in uninstall.php, but the element content is
    executed too, if the file is not found (or not set), so you can
    provied e.g. an error message. -->
    <eval src="uninstall.php"><![CDATA[trigger_error('file (uninstall.php) not found',E_USER_ERROR)]]></eval>
  </uninstall>
 
  <!-- Finally, this is an update (isn't it normally called "update", not "upgrade"?)
  If workes exact like install, only you can have more than one for updates from
  different versions -->
  <update from="1.0a1">
    <create-file destination="$sourcedir" name="testfile"/>
  </update>
</modification>

Dannii

QuoteI think, best would be to have the whole instructions in just one file. I don't know, is there any reason to have executable php code in a mod exept for database querys? I don't think so. Even if it were, you could use the <code> element for it (inline to be easy to edit?)
It couldn't just contain sql queries to send to the database. php code is needed as well, because usually a mod will check how the database is, and then make edits based on that. Even in a mod I've written which has two queries, it needs to check both first, otherwise it would get errors.
"Never imagine yourself not to be otherwise than what it might appear to others that what you were or might have been was not otherwise than what you had been would have appeared to them to be otherwise."

AzaToth

Quote from: eldacar on November 12, 2005, 10:59:07 PM
QuoteI think, best would be to have the whole instructions in just one file. I don't know, is there any reason to have executable php code in a mod exept for database querys? I don't think so. Even if it were, you could use the <code> element for it (inline to be easy to edit?)
It couldn't just contain sql queries to send to the database. php code is needed as well, because usually a mod will check how the database is, and then make edits based on that. Even in a mod I've written which has two queries, it needs to check both first, otherwise it would get errors.
Not if you use mysql 5  8)

Sheepy

Quote from: eldacar on November 12, 2005, 10:59:07 PM
It couldn't just contain sql queries to send to the database. php code is needed as well, because usually a mod will check how the database is, and then make edits based on that.
It depends on what you do.  I have mods that need checks and mods that don't.

Vendetta

Quote from: eldacar on November 12, 2005, 10:59:07 PM
It couldn't just contain sql queries to send to the database. php code is needed as well, because usually a mod will check how the database is, and then make edits based on that.
Its common to add, modify or remove fields or tables, but thats it. So you normaly do a check to the database if the field exists, and if it don't, you add it. I called the test a query, too.

This would look like this:

<sql server="mysql">
  <test for="num_rows" to="0">TEST FOR THIS</test>
  <query version="4">QUERY THIS FOR MYSQL 4</query>
  <query version="5">QUERY THIS FOR MYSQL 5</query>
</sql>


I tryed to write a code to execute this querys:
<?

$test[0] = array(
  'server'=>'mysql',
  'version'=>'0+',
  'func'=>'num_rows',
  'compare'=>'0',
  'sql'=>'TEST FOR THIS'
);
$query[0] = array(
  'server'=>'mysql',
  'version'=>'4',
  'sql'=>'QUERY THIS FOR MYSQL 4'
);
$query[1] = array(
  'server'=>'mysql',
  'version'=>'5',
  'sql'=>'QUERY THIS FOR MYSQL 5'
);
if(ipm_test($test))
{
  ipm_query($query);
}
function ipm_test($arr) { return ipm_query($arr,true); }
function ipm_query($arr,$testIt = false)
{
  foreach ($arr as $key => $elem)
  {
    extract($elem);
    if (empty($version)) $version = '0+';
    if (!ipm_compare_sql_server($server)) continue;
    if (!ipm_compare_sql_version(mysql_get_server_info(),$version)) continue;
    if (!$result = mysql_query($sql)) return false;
    if ($testIt) {
      $func = $server.'_'.$for;
      $func = function_exists($func) ? $func : trigger_error('function ('.$func.') does not exist',E_USER_ERROR);
      switch ($for)
      {
        case 'errno' :
          return ($func() == $to) ? true : false;
        default :
          return ($func($result) == $to) ? true : false;
      }
    }
    return true;
  }
  return false;
}
function ipm_compare_sql_server($server)
{
  // Don't know how to add other databases then MySQL, so we just test for this here
  if (strtoupper($server) == 'MYSQL')
    return true;
  return false;
}
function ipm_compare_sql_version()
{
  if (func_num_args() != 2) {
    trigger_error('compare_version() expects exactly 2 argument',E_USER_WARNING);
    return false;
  }
  foreach (func_get_args() as $key => $elem) {
    $version = func_get_arg($key);
    list($version,$devStatus) = strpos($version,'-') ? explode('-',$version,2) : array($version,'stabil');
    $v[$key] = explode('.',$version);
    $v[$key]['devStatus'] = $devStatus;
  }
  foreach ($v[1] as $key => $elem)
  {
    $passed = ($key == 'devStatus' && $elem == $v[0]['devStatus']) ? true : false;
    $fromHere = (strpos($elem,'+') === strlen($elem)-1) ? true : false;
    $elem = $fromHere ? (int) trim(substr($elem,0,-1)) : (int) $elem;
    $passed = ($elem == '*' || $elem == $v[0][$key] || ($fromHere && $elem < $v[0][$key])) ? true : $passed;
    if (!$passed) break;
  }
  return $passed;
}
?>


Note that I haven't really tested it, just did some pseudo test. I'm afraid I don't have time now to write the package manager I would like to have. I will try it sooner or later, but I hope we can find a real good DTD. So I've rewritten that one again. You may find the file at http://www.forestfactory.de/testarea/allinone.xml [nofollow]

Look at the source to read the DTD with lots of comments

Vendetta

Advertisement: