diff --git a/administrator/components/com_jedchecker/language/en-GB/en-GB.com_jedchecker.ini b/administrator/components/com_jedchecker/language/en-GB/en-GB.com_jedchecker.ini index 9928477..801ba32 100644 --- a/administrator/components/com_jedchecker/language/en-GB/en-GB.com_jedchecker.ini +++ b/administrator/components/com_jedchecker/language/en-GB/en-GB.com_jedchecker.ini @@ -93,3 +93,18 @@ COM_JEDCHECKER_EMPTY_UPLOAD_FIELD="Please, select a zipped file to be uploaded" COM_JEDCHECKER_TOOLBAR_CHECK="Check" COM_JEDCHECKER_TOOLBAR_CLEAR="Clear" COM_JEDCHECKER_CLICK_TO_VIEW_DETAILS="Click to View Details" +COM_JEDCHECKER_MANIFEST="XML Manifests" +COM_JEDCHECKER_MANIFEST_DESC="Validation of extension's XML manifest file" +COM_JEDCHECKER_MANIFEST_UNKNOWN_TYPE="Unknown extension type: %s" +COM_JEDCHECKER_MANIFEST_TYPE_NOT_ACCEPTED="Extension type '%s' is not accepted by JED" +COM_JEDCHECKER_MANIFEST_UNKNOWN_ATTRIBUTE="Node <%1$s> has unknown attribute '%2$s'" +COM_JEDCHECKER_MANIFEST_UNKNOWN_CHILDREN="Node <%s> has unknown child element" +COM_JEDCHECKER_MANIFEST_MISSED_REQUIRED="Node <%1$s> doesn't contain required <%2$s> element" +COM_JEDCHECKER_MANIFEST_MISSED_OPTIONAL="Node <%1$s> doesn't contain optional <%2$s> element" +COM_JEDCHECKER_MANIFEST_MULTIPLE_FOUND="Node <%1$s> contains multiple <%2$s> elements" +COM_JEDCHECKER_MANIFEST_UNKNOWN_CHILD="Node <%1$s> contains unknown <%2$s> element" +COM_JEDCHECKER_MANIFEST_EMPTY_CHILD="Found empty <%s> element" +COM_JEDCHECKER_MANIFEST_MENU_UNUSED_ATTRIBUTE="Menu item attribute '%s' is not used with 'link' attribute" +COM_JEDCHECKER_MANIFEST_MISSED_METHOD_UPGRADE="Without the method="upgrade" attribute the extension package cannot be upgraded" +COM_JEDCHECKER_MANIFEST_MISSED_ATTRIBUTE="The node <%1$s> doesn't contain required '%2$s' attribute" +COM_JEDCHECKER_MANIFEST_UNKNOWN_ATTRIBUTE_VALUE="The node <%1$s> has attribute '%2$s' with unknown value "%3$s"" diff --git a/administrator/components/com_jedchecker/libraries/rules/xmlmanifest.php b/administrator/components/com_jedchecker/libraries/rules/xmlmanifest.php new file mode 100644 index 0000000..5d4f119 --- /dev/null +++ b/administrator/components/com_jedchecker/libraries/rules/xmlmanifest.php @@ -0,0 +1,404 @@ +basedir, '\.xml$', true, true); + + // Iterate through all the xml files + foreach ($files as $file) + { + // Try to check the file + $this->find($file); + } + } + + /** + * Reads a file and validate XML manifest + * + * @param string $file - The path to the file + * + * @return boolean True if the manifest file was found, otherwise False. + */ + protected function find($file) + { + $xml = simplexml_load_file($file); + + // Failed to parse the xml file. + // Assume that this is not a extension manifest + if (!$xml) + { + return false; + } + + // Check if this is an extension manifest + if ($xml->getName() !== 'extension') + { + return false; + } + + // Check extension type + $type = (string) $xml['type']; + + if (!in_array($type, $this->joomlaTypes, true)) + { + $this->report->addError($file, JText::sprintf('COM_JEDCHECKER_MANIFEST_UNKNOWN_TYPE', $type)); + + return true; + } + + // JED allows components, modules, plugins, and packages (as a container) only + if (!in_array($type, $this->jedTypes, true)) + { + $this->report->addError($file, JText::sprintf('COM_JEDCHECKER_MANIFEST_TYPE_NOT_ACCEPTED', $type)); + } + + // Load DTD-like data for this extension type + $jsonFilename = __DIR__ . '/xmlmanifest/dtd_' . $type . '.json'; + + if (!is_file($jsonFilename)) + { + return true; + } + + // Warn if method="upgrade" attribute is not found + if ((string) $xml['method'] !== 'upgrade') + { + $this->report->addWarning($file, JText::_('COM_JEDCHECKER_MANIFEST_MISSED_METHOD_UPGRADE')); + } + + // Check 'client' attribute is "site" or "administrator" (for module/template only) + if ($type === 'module' || $type === 'template') + { + $client = (string) $xml['client']; + + if (!isset($xml['client'])) + { + $this->report->addError($file, JText::sprintf('COM_JEDCHECKER_MANIFEST_MISSED_ATTRIBUTE', $xml->getName(), 'client')); + } + elseif ($client !== 'site' && $client !== 'administrator') + { + $this->report->addError($file, JText::sprintf('COM_JEDCHECKER_MANIFEST_UNKNOWN_ATTRIBUTE_VALUE', $xml->getName(), 'client', $client)); + } + } + + $data = json_decode(file_get_contents($jsonFilename), true); + $this->DTDNodeRules = $data['nodes']; + $this->DTDAttrRules = $data['attributes']; + + $this->errors = array(); + $this->warnings = array(); + $this->infos = array(); + + // Validate manifest + $this->validateXml($xml, 'extension'); + + if (count($this->errors)) + { + $this->report->addError($file, implode('
', $this->errors)); + } + + if (count($this->warnings)) + { + $this->report->addWarning($file, implode('
', $this->warnings)); + } + + if (count($this->infos)) + { + $this->report->addInfo($file, implode('
', $this->infos)); + } + + // All checks passed. Return true + return true; + } + + /** + * @param SimpleXMLElement $node XML node object + * @param string $ruleset rulest name in the DTD array + * + * @return void + */ + protected function validateXml($node, $ruleset) + { + // Get node name + $name = $node->getName(); + + // Check attributes + $DTDattributes = isset($this->DTDAttrRules[$ruleset]) ? $this->DTDAttrRules[$ruleset] : array(); + + if (count($DTDattributes) === 0) + { + // No known attributes for this node + foreach ($node->attributes() as $attr) + { + $this->infos[] = JText::sprintf('COM_JEDCHECKER_MANIFEST_UNKNOWN_ATTRIBUTE', $name, (string) $attr->getName()); + } + } + elseif ($DTDattributes[0] !== '*') // Skip node with arbitrary attributes (e.g. "field") + { + foreach ($node->attributes() as $attr) + { + $attrName = (string) $attr->getName(); + + if (!in_array($attrName, $DTDattributes, true)) + { + // The node has unknown attribute + $this->infos[] = JText::sprintf('COM_JEDCHECKER_MANIFEST_UNKNOWN_ATTRIBUTE', $name, $attrName); + } + } + } + + // Check children nodes + $DTDchildRules = isset($this->DTDNodeRules[$ruleset]) ? $this->DTDNodeRules[$ruleset] : array(); + + // Child node name to ruleset name mapping + $DTDchildToRule = array(); + + if (count($DTDchildRules) === 0) + { + // No known children for this node + if ($node->count() > 0) + { + $this->infos[] = JText::sprintf('COM_JEDCHECKER_MANIFEST_UNKNOWN_CHILDREN', $name); + } + } + elseif (!isset($DTDchildRules['*'])) // Skip node with arbitrary children + { + // 1) check required single elements + foreach ($DTDchildRules as $childRuleset => $mode) + { + $child = $childRuleset; + + if (strpos($child, ':') !== false) + { + // Split ruleset name into a prefix and the child node name + list ($prefix, $child) = explode(':', $child, 2); + } + + // Populate node-to-ruleset mapping + $DTDchildToRule[$child] = $childRuleset; + + $count = $node->$child->count(); + + switch ($mode) + { + case '!': + if ($count === 0) + { + // The node doesn't contain required child element + $this->errors[] = JText::sprintf('COM_JEDCHECKER_MANIFEST_MISSED_REQUIRED', $name, $child); + } + elseif ($count > 1) + { + // The node contains multiple child elements when single only is expected + $this->errors[] = JText::sprintf('COM_JEDCHECKER_MANIFEST_MULTIPLE_FOUND', $name, $child); + } + + break; + + case '=': + if ($count === 0) + { + // The node doesn't contain optional child element + $this->infos[] = JText::sprintf('COM_JEDCHECKER_MANIFEST_MISSED_OPTIONAL', $name, $child); + } + elseif ($count > 1) + { + // The node contains multiple child elements when single only is expected + $this->warnings[] = JText::sprintf('COM_JEDCHECKER_MANIFEST_MULTIPLE_FOUND', $name, $child); + } + + break; + } + } + + // 2) check unknown/multiple elements + + // Collect unique child node names + $childNames = array(); + + foreach ($node as $child) + { + $childNames[$child->getName()] = 1; + } + + $childNames = array_keys($childNames); + + foreach ($childNames as $child) + { + if (!isset($DTDchildToRule[$child])) + { + // The node contains unknown child element + $this->infos[] = JText::sprintf('COM_JEDCHECKER_MANIFEST_UNKNOWN_CHILD', $name, $child); + } + else + { + if ($DTDchildRules[$DTDchildToRule[$child]] === '?' && $node->$child->count() > 1) + { + // The node contains multiple child elements when single only is expected + $this->errors[] = JText::sprintf('COM_JEDCHECKER_MANIFEST_MULTIPLE_FOUND', $name, $child); + } + } + } + + // 3) check empty elements + foreach ($node as $child) + { + if ($child->count() === 0 && $child->attributes()->count() === 0 && (string) $child === '') + { + $this->infos[] = JText::sprintf('COM_JEDCHECKER_MANIFEST_EMPTY_CHILD', $child->getName()); + } + } + } + + // Extra checks (if exist) + $method = 'validateXml' . $name; + + if (method_exists($this, $method)) + { + $this->$method($node); + } + + // Recursion + foreach ($node as $child) + { + $childName = $child->getName(); + + if (isset($DTDchildToRule[$childName])) + { + $this->validateXml($child, $DTDchildToRule[$childName]); + } + } + } + + /** + * Extra check for menu nodes + * @param SimpleXMLElement $node XML node + * + * @return void + */ + protected function validateXmlMenu($node) + { + if (isset($node['link'])) + { + // The "link" attribute overrides any other link-related attributes (warn if they present) + $skipAttrs = array('act', 'controller', 'layout', 'sub', 'task', 'view'); + + foreach ($node->attributes() as $attr) + { + $attrName = $attr->getName(); + + if (in_array($attrName, $skipAttrs, true)) + { + $this->warnings[] = JText::sprintf('COM_JEDCHECKER_MANIFEST_MENU_UNUSED_ATTRIBUTE', $attrName); + } + } + } + } +} diff --git a/administrator/components/com_jedchecker/libraries/rules/xmlmanifest/dtd_component.json b/administrator/components/com_jedchecker/libraries/rules/xmlmanifest/dtd_component.json new file mode 100644 index 0000000..5ca36f4 --- /dev/null +++ b/administrator/components/com_jedchecker/libraries/rules/xmlmanifest/dtd_component.json @@ -0,0 +1,120 @@ +{ + "nodes": { + "extension": { + "name": "!", + "element": "?", + "creationDate": "=", + "author": "=", + "authorEmail": "=", + "authorUrl": "=", + "copyright": "=", + "version": "!", + "description": "=", + "license": "?", + "scriptfile": "?", + "install": "?", + "update": "?", + "uninstall": "?", + "files": "?", + "languages": "?", + "media": "?", + "administration": "?", + "updateservers": "?", + "dlid": "?", + "config": "?", + "namespace": "?" + }, + "administration": { + "menu": "?", + "submenu": "?", + "files": "=", + "languages": "?", + "media": "?" + }, + "files": { + "filename": "*", + "files:file": "*", + "folder": "*" + }, + "languages": { + "language": "*" + }, + "media": { + "filename": "*", + "media:file": "*", + "folder": "*" + }, + "submenu": { + "menu": "*" + }, + "install": { + "sql": "*" + }, + "update": { + "sql": "*", + "schemas": "*" + }, + "uninstall": { + "sql": "*" + }, + "sql": { + "file": "*" + }, + "schemas": { + "schemapath": "*" + }, + "updateservers": { + "server": "*" + } + }, + "attributes": { + "extension": [ + "client", + "method", + "overwrite", + "type", + "version" + ], + "files": [ + "folder" + ], + "languages": [ + "folder" + ], + "language": [ + "client", + "tag" + ], + "media": [ + "destination", + "folder" + ], + "menu": [ + "act", + "controller", + "hidden", + "img", + "layout", + "link", + "sub", + "task", + "view" + ], + "file": [ + "charset", + "driver" + ], + "server": [ + "name", + "priority", + "type" + ], + "namespace": [ + "path" + ], + "dlid": [ + "prefix", + "suffix" + ] + } +} diff --git a/administrator/components/com_jedchecker/libraries/rules/xmlmanifest/dtd_file.json b/administrator/components/com_jedchecker/libraries/rules/xmlmanifest/dtd_file.json new file mode 100644 index 0000000..902338e --- /dev/null +++ b/administrator/components/com_jedchecker/libraries/rules/xmlmanifest/dtd_file.json @@ -0,0 +1,86 @@ +{ + "nodes": { + "extension": { + "name": "!", + "creationDate": "=", + "author": "=", + "authorEmail": "=", + "authorUrl": "=", + "copyright": "=", + "version": "!", + "description": "=", + "license": "?", + "scriptfile": "?", + "install": "?", + "update": "?", + "uninstall": "?", + "fileset": "!", + "languages": "?", + "updateservers": "?", + "dlid": "?" + }, + "fileset": { + "files": "*" + }, + "files": { + "filename": "*", + "files:file": "*", + "folder": "*" + }, + "languages": { + "language": "*" + }, + "install": { + "sql": "*" + }, + "update": { + "sql": "*", + "schemas": "*" + }, + "uninstall": { + "sql": "*" + }, + "sql": { + "file": "*" + }, + "schemas": { + "schemapath": "*" + }, + "updateservers": { + "server": "*" + } + }, + "attributes": { + "extension": [ + "client", + "method", + "overwrite", + "type", + "version" + ], + "files": [ + "folder", + "target" + ], + "languages": [ + "folder" + ], + "language": [ + "client", + "tag" + ], + "file": [ + "charset", + "driver" + ], + "server": [ + "name", + "priority", + "type" + ], + "dlid": [ + "prefix", + "suffix" + ] + } +} diff --git a/administrator/components/com_jedchecker/libraries/rules/xmlmanifest/dtd_language.json b/administrator/components/com_jedchecker/libraries/rules/xmlmanifest/dtd_language.json new file mode 100644 index 0000000..ad20a0b --- /dev/null +++ b/administrator/components/com_jedchecker/libraries/rules/xmlmanifest/dtd_language.json @@ -0,0 +1,68 @@ +{ + "nodes": { + "extension": { + "name": "!", + "tag": "!", + "creationDate": "=", + "author": "=", + "authorEmail": "=", + "authorUrl": "=", + "copyright": "=", + "version": "!", + "description": "=", + "license": "?", + "files": "!", + "media": "?", + "fonts": "?", + "update": "?", + "updateservers": "?", + "dlid": "?" + }, + "files": { + "filename": "*", + "files:file": "*", + "folder": "*" + }, + "media": { + "filename": "*", + "media:file": "*", + "folder": "*" + }, + "fonts": { + "filename": "*", + "fonts:file": "*", + "folder": "*" + }, + "updateservers": { + "server": "*" + } + }, + "attributes": { + "extension": [ + "client", + "method", + "overwrite", + "type", + "version" + ], + "files": [ + "folder" + ], + "media": [ + "destination", + "folder" + ], + "fonts": [ + "folder" + ], + "server": [ + "name", + "priority", + "type" + ], + "dlid": [ + "prefix", + "suffix" + ] + } +} diff --git a/administrator/components/com_jedchecker/libraries/rules/xmlmanifest/dtd_library.json b/administrator/components/com_jedchecker/libraries/rules/xmlmanifest/dtd_library.json new file mode 100644 index 0000000..b886e4f --- /dev/null +++ b/administrator/components/com_jedchecker/libraries/rules/xmlmanifest/dtd_library.json @@ -0,0 +1,95 @@ +{ + "nodes": { + "extension": { + "name": "!", + "libraryname": "!", + "creationDate": "=", + "author": "=", + "authorEmail": "=", + "authorUrl": "=", + "copyright": "=", + "version": "!", + "description": "=", + "license": "?", + "packager": "?", + "packagerurl": "?", + "scriptfile": "?", + "install": "?", + "update": "?", + "uninstall": "?", + "files": "?", + "languages": "?", + "media": "?", + "updateservers": "?", + "dlid": "?" + }, + "files": { + "filename": "*", + "files:file": "*", + "folder": "*" + }, + "languages": { + "language": "*" + }, + "media": { + "filename": "*", + "media:file": "*", + "folder": "*" + }, + "install": { + "sql": "*" + }, + "update": { + "sql": "*", + "schemas": "*" + }, + "uninstall": { + "sql": "*" + }, + "sql": { + "file": "*" + }, + "schemas": { + "schemapath": "*" + }, + "updateservers": { + "server": "*" + } + }, + "attributes": { + "extension": [ + "client", + "method", + "overwrite", + "type", + "version" + ], + "files": [ + "folder" + ], + "languages": [ + "folder" + ], + "language": [ + "client", + "tag" + ], + "media": [ + "destination", + "folder" + ], + "file": [ + "charset", + "driver" + ], + "server": [ + "name", + "priority", + "type" + ], + "dlid": [ + "prefix", + "suffix" + ] + } +} diff --git a/administrator/components/com_jedchecker/libraries/rules/xmlmanifest/dtd_module.json b/administrator/components/com_jedchecker/libraries/rules/xmlmanifest/dtd_module.json new file mode 100644 index 0000000..3becd97 --- /dev/null +++ b/administrator/components/com_jedchecker/libraries/rules/xmlmanifest/dtd_module.json @@ -0,0 +1,144 @@ +{ + "nodes": { + "extension": { + "name": "!", + "element": "?", + "creationDate": "=", + "author": "=", + "authorEmail": "=", + "authorUrl": "=", + "copyright": "=", + "version": "!", + "description": "=", + "license": "?", + "scriptfile": "?", + "install": "?", + "update": "?", + "uninstall": "?", + "files": "?", + "languages": "?", + "media": "?", + "updateservers": "?", + "dlid": "?", + "config": "?", + "namespace": "?" + }, + "files": { + "filename": "*", + "files:file": "*", + "folder": "*" + }, + "languages": { + "language": "*" + }, + "media": { + "filename": "*", + "media:file": "*", + "folder": "*" + }, + "install": { + "sql": "*" + }, + "update": { + "sql": "*", + "schemas": "*" + }, + "uninstall": { + "sql": "*" + }, + "sql": { + "file": "*" + }, + "updateservers": { + "server": "*" + }, + "config": { + "fields": "!" + }, + "fields": { + "fieldset": "+" + }, + "fieldset": { + "field": "+" + }, + "field": { + "*": "*" + } + }, + "attributes": { + "extension": [ + "client", + "method", + "overwrite", + "type", + "version" + ], + "files": [ + "folder" + ], + "filename": [ + "module" + ], + "files:file": [ + "module" + ], + "languages": [ + "folder" + ], + "language": [ + "client", + "tag" + ], + "media": [ + "destination", + "folder" + ], + "file": [ + "charset", + "driver" + ], + "server": [ + "name", + "priority", + "type" + ], + "namespace": [ + "path" + ], + "config": [ + "addfieldpath", + "addfieldprefix", + "addformpath", + "addformprefix", + "addrulepath", + "addruleprefix" + ], + "fields": [ + "addfieldpath", + "addfieldprefix", + "addformpath", + "addformprefix", + "addrulepath", + "addruleprefix", + "name" + ], + "fieldset": [ + "addfieldpath", + "addfieldprefix", + "addformpath", + "addformprefix", + "addrulepath", + "addruleprefix", + "description", + "label", + "name" + ], + "field": [ + "*" + ], + "dlid": [ + "prefix", + "suffix" + ] + } +} diff --git a/administrator/components/com_jedchecker/libraries/rules/xmlmanifest/dtd_package.json b/administrator/components/com_jedchecker/libraries/rules/xmlmanifest/dtd_package.json new file mode 100644 index 0000000..f9a7930 --- /dev/null +++ b/administrator/components/com_jedchecker/libraries/rules/xmlmanifest/dtd_package.json @@ -0,0 +1,76 @@ +{ + "nodes": { + "extension": { + "name": "!", + "packagename": "!", + "creationDate": "=", + "author": "=", + "authorEmail": "=", + "authorUrl": "=", + "copyright": "=", + "version": "!", + "description": "=", + "license": "?", + "scriptfile": "?", + "update": "?", + "files": "?", + "languages": "?", + "updateservers": "?", + "dlid": "?", + "url": "?", + "packager": "?", + "packagerurl": "?", + "blockChildUninstall": "?" + }, + "files": { + "filename": "*", + "files:file": "*", + "folder": "*" + }, + "languages": { + "language": "*" + }, + "updateservers": { + "server": "*" + } + }, + "attributes": { + "extension": [ + "method", + "overwrite", + "type", + "version" + ], + "files": [ + "folder" + ], + "files:file": [ + "client", + "group", + "id", + "type" + ], + "filename": [ + "client", + "group", + "id", + "type" + ], + "languages": [ + "folder" + ], + "language": [ + "client", + "tag" + ], + "server": [ + "name", + "priority", + "type" + ], + "dlid": [ + "prefix", + "suffix" + ] + } +} diff --git a/administrator/components/com_jedchecker/libraries/rules/xmlmanifest/dtd_plugin.json b/administrator/components/com_jedchecker/libraries/rules/xmlmanifest/dtd_plugin.json new file mode 100644 index 0000000..1b5b555 --- /dev/null +++ b/administrator/components/com_jedchecker/libraries/rules/xmlmanifest/dtd_plugin.json @@ -0,0 +1,145 @@ +{ + "nodes": { + "extension": { + "name": "!", + "element": "?", + "creationDate": "=", + "author": "=", + "authorEmail": "=", + "authorUrl": "=", + "copyright": "=", + "version": "!", + "description": "=", + "license": "?", + "scriptfile": "?", + "install": "?", + "update": "?", + "uninstall": "?", + "files": "?", + "languages": "?", + "media": "?", + "updateservers": "?", + "dlid": "?", + "config": "?", + "namespace": "?" + }, + "files": { + "filename": "*", + "files:file": "*", + "folder": "*" + }, + "languages": { + "language": "*" + }, + "media": { + "filename": "*", + "media:file": "*", + "folder": "*" + }, + "install": { + "sql": "*" + }, + "update": { + "sql": "*", + "schemas": "*" + }, + "uninstall": { + "sql": "*" + }, + "sql": { + "file": "*" + }, + "updateservers": { + "server": "*" + }, + "config": { + "fields": "!" + }, + "fields": { + "fieldset": "+" + }, + "fieldset": { + "field": "+" + }, + "field": { + "*": "*" + } + }, + "attributes": { + "extension": [ + "client", + "group", + "method", + "overwrite", + "type", + "version" + ], + "files": [ + "folder" + ], + "filename": [ + "plugin" + ], + "files:file": [ + "plugin" + ], + "languages": [ + "folder" + ], + "language": [ + "client", + "tag" + ], + "media": [ + "destination", + "folder" + ], + "file": [ + "charset", + "driver" + ], + "server": [ + "name", + "priority", + "type" + ], + "namespace": [ + "path" + ], + "config": [ + "addfieldpath", + "addfieldprefix", + "addformpath", + "addformprefix", + "addrulepath", + "addruleprefix" + ], + "fields": [ + "addfieldpath", + "addfieldprefix", + "addformpath", + "addformprefix", + "addrulepath", + "addruleprefix", + "name" + ], + "fieldset": [ + "addfieldpath", + "addfieldprefix", + "addformpath", + "addformprefix", + "addrulepath", + "addruleprefix", + "description", + "label", + "name" + ], + "field": [ + "*" + ], + "dlid": [ + "prefix", + "suffix" + ] + } +} diff --git a/administrator/components/com_jedchecker/libraries/rules/xmlmanifest/dtd_template.json b/administrator/components/com_jedchecker/libraries/rules/xmlmanifest/dtd_template.json new file mode 100644 index 0000000..dbceb31 --- /dev/null +++ b/administrator/components/com_jedchecker/libraries/rules/xmlmanifest/dtd_template.json @@ -0,0 +1,119 @@ +{ + "nodes": { + "extension": { + "name": "!", + "element": "?", + "creationDate": "=", + "author": "=", + "authorEmail": "=", + "authorUrl": "=", + "copyright": "=", + "version": "!", + "description": "=", + "license": "?", + "scriptfile": "?", + "install": "?", + "update": "?", + "uninstall": "?", + "files": "?", + "images": "?", + "css": "?", + "languages": "?", + "media": "?", + "positions": "=", + "updateservers": "?", + "dlid": "?", + "config": "?" + }, + "positions": { + "position": "*" + }, + "files": { + "filename": "*", + "files:file": "*", + "folder": "*" + }, + "images": { + "filename": "*", + "images:file": "*", + "folder": "*" + }, + "css": { + "filename": "*", + "css:file": "*", + "folder": "*" + }, + "languages": { + "language": "*" + }, + "media": { + "filename": "*", + "media:file": "*", + "folder": "*" + }, + "install": { + "sql": "*" + }, + "update": { + "sql": "*", + "schemas": "*" + }, + "uninstall": { + "sql": "*" + }, + "sql": { + "file": "*" + }, + "schemas": { + "schemapath": "*" + }, + "updateservers": { + "server": "*" + } + }, + "attributes": { + "extension": [ + "client", + "method", + "overwrite", + "type", + "version" + ], + "files": [ + "folder" + ], + "images": [ + "folder" + ], + "css": [ + "folder" + ], + "languages": [ + "folder" + ], + "language": [ + "client", + "tag" + ], + "position": [ + "value" + ], + "media": [ + "destination", + "folder" + ], + "file": [ + "charset", + "driver" + ], + "server": [ + "name", + "priority", + "type" + ], + "dlid": [ + "prefix", + "suffix" + ] + } +}