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 ...
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
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>
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.
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
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>®2; ®3;</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>
LainaaI 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.
Lainaus käyttäjältä: eldacar - marraskuu 12, 2005, 10:59:07 IP
LainaaI 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)
Lainaus käyttäjältä: eldacar - marraskuu 12, 2005, 10:59:07 IP
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.
Lainaus käyttäjältä: eldacar - marraskuu 12, 2005, 10:59:07 IP
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
Look at the source to read the DTD with lots of comments
Vendetta