diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..6bd4eaf
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,5 @@
+# Set default behaviour, in case users don't have core.autocrlf set.
+* text=auto
+*.xml text
+*.xsd text
+*.dtd text
\ No newline at end of file
diff --git a/README.md b/README.md
index 4182ac0..c9106aa 100644
--- a/README.md
+++ b/README.md
@@ -2,3 +2,4 @@ schemas
=======
Schemas for the Joomla! CMS
+
diff --git a/xsd/README.md b/xsd/README.md
new file mode 100644
index 0000000..6d17e75
--- /dev/null
+++ b/xsd/README.md
@@ -0,0 +1,7 @@
+
+call validate like this
+
+phpunit --bootstrap bootstrap.php validate.php
+
+
+
diff --git a/xsd/bootstrap.php b/xsd/bootstrap.php
new file mode 100644
index 0000000..99f869c
--- /dev/null
+++ b/xsd/bootstrap.php
@@ -0,0 +1,5 @@
+
+
+
+
+ An introduction text.
+
+ ### a title
+ * a list entry
+ * another list entry
+
+ ### another title
+ Some text.
+
+
+
+ http://www.acme.com
+ https://www.acme.com/download/
+ https://www.acme.com/demo/
+ https://www.acme.com/doc/
+ https://www.acme.com/forums/
+ https://www.acme.com/licence/
+
+ 1.0.0
+
+
+ 25
+ 30
+
+
\ No newline at end of file
diff --git a/xsd/jed/jedupdate.xsd b/xsd/jed/jedupdate.xsd
new file mode 100644
index 0000000..cc6fd8b
--- /dev/null
+++ b/xsd/jed/jedupdate.xsd
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This attribute describes the version.
+
+
+
+
+
+
+
+
diff --git a/xsd/jed/validate.php b/xsd/jed/validate.php
new file mode 100644
index 0000000..46678ed
--- /dev/null
+++ b/xsd/jed/validate.php
@@ -0,0 +1,66 @@
+xml = new DOMDocument();
+ libxml_use_internal_errors(true);
+ }
+
+ public function testValidate_forFileJedUpdateSample_expectValidTrue()
+ {
+ // Arrange
+ $jedupdates = array();
+ $jedupdates = dirname(__FILE__) . "/jedupdate-sample.xml";
+ $schema = dirname(__FILE__) . "/jedupdate.xsd";
+
+ // Act
+ foreach ($jedupdates as $jedupdate) {
+ $this->xml->load($jedupdate);
+ $schemaValidate = $this->xml->schemaValidate($schema);
+
+ // Assert
+ $this->libxml_display_errors();
+ $this->assertEquals(TRUE, $schemaValidate);
+ }
+ }
+
+ function libxml_display_error($error)
+ {
+ $return = "
\n";
+ switch ($error->level) {
+ case LIBXML_ERR_WARNING:
+ $return .= "Warning $error->code: ";
+ break;
+ case LIBXML_ERR_ERROR:
+ $return .= "Error $error->code: ";
+ break;
+ case LIBXML_ERR_FATAL:
+ $return .= "Fatal Error $error->code: ";
+ break;
+ }
+ $return .= trim($error->message);
+ if ($error->file) {
+ $return .= " in $error->file";
+ }
+ $return .= " on line $error->line\n";
+
+ return $return;
+ }
+
+ private function libxml_display_errors()
+ {
+ $errors = libxml_get_errors();
+
+ self::$errors = self::$errors + sizeof($errors);
+
+ foreach ($errors as $error) {
+ print $this->libxml_display_error($error);
+ }
+ libxml_clear_errors();
+ }
+
+}
\ No newline at end of file
diff --git a/xsd/v3/common.xsd b/xsd/v3/common.xsd
new file mode 100644
index 0000000..af513ae
--- /dev/null
+++ b/xsd/v3/common.xsd
@@ -0,0 +1,678 @@
+
+
+
+
+
+ The default value install will be also used if the method attribute is not used. The install value means the
+ installer will gracefully stop if it finds any existing file/folder of the new extension.
+
+
+
+
+
+
+
+
+
+
+ This attribute describes the type of the extension for the installer. Based on this type further requirements to
+ sub-tags apply.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ String that identifies the version of Joomla for which this extension is developed.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Denote if a field is required or not
+
+
+
+
+
+
+
+
+
+ String that identifies the version of your extension.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The value inside the tag is the menu's label. Unlike Joomla! 1.5, you can not use a natural language string. For example, if you would enter "Example Component" instead of COM_EXAMPLE, it would result in your component name appearing as example-component in the menu and you would be unable to provide a translation. In order to provide a translation you need to create a file named en-GB.com_example.sys.ini in administrator/languages/en-GB
+
+
+
+ The (relative) path to an image (16x16 pixels) to appear beside the menu item.
+ Must be an url compatible as a file too (e.g. no spaces) !
+
+
+
+
+ A link to send the user to when the menu item is clicked.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The administration section is defined in this element. Since only components apply to both the site and the administrator, only component manifests can include this element.
+
+
+
+
+
+
+
+
+
+
+
+
+ This element may contain one or more server element, each describing a location to fetch updates from.
+
+
+
+
+
+
+
+
+
+
+ This element define one server to fetch updates from.
+
+
+
+
+
+
+
+ The name of the update server.
+
+
+
+
+
+
+
+
+
+
+ The update server type.
+
+
+
+
+
+
+
+
+ The priority of the update server.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This defines the directory that contains SQL files for component incremental database updates. You may choose to put
+ the SQL files somewhere else in your component release, but make sure their location is documented here.
+
+
+
+
+
+
+
+
+
+ This tag is considered to be deprecated since Joomla! 1.6. We encourage you to put extension 's language files in the extension folder and Joomla! is responsible for the loading of required language files.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ describes the configuration options for the extension. If applicable, the options will be shown by the appropriate Manager (Plugin Manager, Module Manager or Template Manager). Configuration options can also be defined in a separate file named config.xml. Its root element should be config.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The client attribute allows you to specify for which application client the new module is available.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Specify a directory in the ZIP package to copy from. This will copy an entire folder at once
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Dependencies are not currently supported, but were added for that possibility in the future.
+
+
+
+
+
+
+
+
+
+ Dependencies are not currently supported, but were added for that possibility in the future.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This attribute describes the type of the extension for the installer. Based on this type further requirements to
+ sub-tags apply.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Defines a string subset safe in most file systems as file name (excluding path).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Validate filepath
+
+
+
+
+
+
+
+
+
+ Validate filepath
+
+
+
+
+
+
+
+
diff --git a/xsd/v3/component.xsd b/xsd/v3/component.xsd
new file mode 100644
index 0000000..4f548f5
--- /dev/null
+++ b/xsd/v3/component.xsd
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/xsd/v3/language.xsd b/xsd/v3/language.xsd
new file mode 100644
index 0000000..103153e
--- /dev/null
+++ b/xsd/v3/language.xsd
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Validate weekEnd
+
+
+
+
+
+
+
diff --git a/xsd/v3/library.xsd b/xsd/v3/library.xsd
new file mode 100644
index 0000000..be96ad7
--- /dev/null
+++ b/xsd/v3/library.xsd
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/xsd/v3/module.xsd b/xsd/v3/module.xsd
new file mode 100644
index 0000000..432bedd
--- /dev/null
+++ b/xsd/v3/module.xsd
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/xsd/v3/package.xsd b/xsd/v3/package.xsd
new file mode 100644
index 0000000..569cd24
--- /dev/null
+++ b/xsd/v3/package.xsd
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Validate filepath
+
+
+
+
+
+
+
+
diff --git a/xsd/v3/plugin.xsd b/xsd/v3/plugin.xsd
new file mode 100644
index 0000000..a4e34cf
--- /dev/null
+++ b/xsd/v3/plugin.xsd
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+ author's name (e.g. Joomla! Project)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The group name specifies for which group of plugins the new plugin is available. The existing groups are the folder names within the directory /plugins.
+ The installer will create new folder names for group names that do not exist yet.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/xsd/v3/templateDetails.xsd b/xsd/v3/templateDetails.xsd
new file mode 100644
index 0000000..fea09ed
--- /dev/null
+++ b/xsd/v3/templateDetails.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/xsd/validate.php b/xsd/validate.php
new file mode 100644
index 0000000..1c9b28e
--- /dev/null
+++ b/xsd/validate.php
@@ -0,0 +1,228 @@
+xsdVersion = "v3";
+ } else {
+ $this->xsdVersion = $joomla_version;
+ }
+ $this->xml = new DOMDocument();
+ libxml_use_internal_errors(true);
+ }
+
+ public static function setUpBeforeClass()
+ {
+ self::$count = 0;
+ self::$errors = 0;
+ }
+
+ public static function tearDownAfterClass()
+ {
+ printf("Analysed " . self::$count . " Joomla manifest(s), found ".self::$errors . " error(s).");
+ }
+
+ public function testValidate_forComponentManifests_expectValidTrue()
+ {
+ // Arrange
+ $manifests = $this->getManifests("components", 'type="component"');
+ $schema = dirname(__FILE__) . "/" . $this->xsdVersion . "/component.xsd";
+
+ // Act
+ foreach ($manifests as $manifest) {
+ $this->xml->load($manifest);
+ $schemaValidate = $this->xml->schemaValidate($schema);
+
+ // Assert
+ $this->libxml_display_errors();
+
+ //TODO can do this or first invalid manifest will break the build
+ //$this->assertEquals(TRUE, $schemaValidate);
+ }
+ }
+
+ public function testValidate_forModuleManifests_expectValidTrue()
+ {
+ // Arrange
+ $manifests = $this->getManifests("modules", 'type="module"');
+ $schema = dirname(__FILE__) . "/" . $this->xsdVersion . "/module.xsd";
+
+ // Act
+ foreach ($manifests as $manifest) {
+ $this->xml->load($manifest);
+ $schemaValidate = $this->xml->schemaValidate($schema);
+
+ // Assert
+ $this->libxml_display_errors();
+
+ //TODO can do this or first invalid manifest will break the build
+ //$this->assertEquals(TRUE, $schemaValidate);
+ }
+ }
+
+ public function testValidate_forPluginsManifests_expectValidTrue()
+ {
+ // Arrange
+ $manifests = $this->getManifests("plugins", 'type="plugin"');
+ $schema = dirname(__FILE__) . "/" . $this->xsdVersion . "/plugin.xsd";
+
+ // Act
+ foreach ($manifests as $manifest) {
+ $this->xml->load($manifest);
+ $schemaValidate = $this->xml->schemaValidate($schema);
+
+ // Assert
+ $this->libxml_display_errors();
+
+ //TODO can do this or first invalid manifest will break the build
+ //$this->assertEquals(TRUE, $schemaValidate);
+ }
+ }
+
+ public function testValidate_forTemplatesManifests_expectValidTrue()
+ {
+ // Arrange
+ $manifests = $this->getManifests("templates", 'type="template"');
+ $schema = dirname(__FILE__) . "/" . $this->xsdVersion . "/template.xsd";
+
+ // Act
+ foreach ($manifests as $manifest) {
+ $this->xml->load($manifest);
+ $schemaValidate = $this->xml->schemaValidate($schema);
+
+ // Assert
+ $this->libxml_display_errors();
+
+ //TODO can do this or first invalid manifest will break the build
+ //$this->assertEquals(TRUE, $schemaValidate);
+ }
+ }
+
+ public function testValidate_forLibraryManifests_expectValidTrue()
+ {
+ // Arrange
+ $manifests = $this->getManifests("manifests/libraries", 'type="library"');
+ $schema = dirname(__FILE__) . "/" . $this->xsdVersion . "/library.xsd";
+
+ // Act
+ foreach ($manifests as $manifest) {
+ $this->xml->load($manifest);
+ $schemaValidate = $this->xml->schemaValidate($schema);
+
+ // Assert
+ $this->libxml_display_errors();
+
+ //TODO can do this or first invalid manifest will break the build
+ //$this->assertEquals(TRUE, $schemaValidate);
+ }
+ }
+
+ public function testValidate_forLanguagesManifests_expectValidTrue()
+ {
+ // Arrange
+ $manifests = $this->getManifests("language", "xsdVersion . "/language.xsd";
+
+ // Act
+ foreach ($manifests as $manifest) {
+ $this->xml->load($manifest);
+ $schemaValidate = $this->xml->schemaValidate($schema);
+
+ // Assert
+ $this->libxml_display_errors();
+
+ //TODO can do this or first invalid manifest will break the build
+ //$this->assertEquals(TRUE, $schemaValidate);
+ }
+ }
+
+ function libxml_display_error($error)
+ {
+ $return = "
\n";
+ switch ($error->level) {
+ case LIBXML_ERR_WARNING:
+ $return .= "Warning $error->code: ";
+ break;
+ case LIBXML_ERR_ERROR:
+ $return .= "Error $error->code: ";
+ break;
+ case LIBXML_ERR_FATAL:
+ $return .= "Fatal Error $error->code: ";
+ break;
+ }
+ $return .= trim($error->message);
+ if ($error->file) {
+ $return .= " in $error->file";
+ }
+ $return .= " on line $error->line\n";
+
+ return $return;
+ }
+
+ private function libxml_display_errors()
+ {
+ $errors = libxml_get_errors();
+
+ self::$errors = self::$errors + sizeof($errors);
+
+ foreach ($errors as $error) {
+ print $this->libxml_display_error($error);
+ }
+ libxml_clear_errors();
+ }
+
+ private function getManifests($folder = 'components', $extensionType)
+ {
+ //from bootstrap file
+ global $joomla_root;
+
+ $adminManifests = array();
+ $adminPath = $joomla_root . 'administrator/' . $folder . '/';
+ if (file_exists($adminPath)) {
+ $adminManifests = $this->getManifest($extensionType, $adminPath);
+ }
+
+ $siteManifests = array();
+ $sitePath = $joomla_root . $folder . '/';
+ if (file_exists($sitePath)) {
+ $siteManifests = $this->getManifest($extensionType, $sitePath);
+ }
+
+ return array_merge($adminManifests, $siteManifests);
+ }
+
+ private function getManifest($extensionType, $path)
+ {
+ $manifests = array();
+
+ $directory = new RecursiveDirectoryIterator($path);
+ $iterator = new RecursiveIteratorIterator($directory);
+ $regex = new RegexIterator($iterator, '/^.+\.xml$/i', RecursiveRegexIterator::GET_MATCH);
+
+ $iterator_to_array = iterator_to_array($regex);
+ foreach ($iterator_to_array as $key => $filename) {
+ //TODO poor man approach
+ $file = fopen($key, 'r');
+ $header = fread($file, 150);
+ if (strpos($header, $extensionType) !== FALSE) {
+ $manifests[] = $key;
+ }
+ }
+
+ self::$count = self::$count + sizeof($manifests);
+
+ return $manifests;
+ }
+
+}
\ No newline at end of file