Skip to content

Instantly share code, notes, and snippets.

@designermonkey
Created October 6, 2011 19:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save designermonkey/1268374 to your computer and use it in GitHub Desktop.
Save designermonkey/1268374 to your computer and use it in GitHub Desktop.
XSD: Validate Extension XML
<?xml version="1.0" encoding="UTF-8" ?>
<extension id="your_extension_path" xmlns="http://symphony-cms.com/extensions/1.0">
<!-- string, required -->
<name>Your Extension Name</name>
<!-- string, required (@lang optional) -->
<description lang="en">Add the description to your extension here.</description>
<!-- string, required (@type=github|codebase|other, for future repo posibilities?) (must be Github for clever API things to happen) -->
<repo type="github">https://github.com/username/your_extension_path</repo>
<!-- string, optional (@type=homepage|wiki|other?) -->
<url type="homepage">http://symphony-cms.com/downloads/extensions/your_extension_path</url>
<!-- list, optional (Symphony to define suggested vocabulary, suggest leaving this out initially?) -->
<types>
<type>Event</type>
<type>Field</type>
<type>Interface</type>
<type>Multimedia</type>
<type>Text Formatter</type>
<type>Translation</type>
<type>Workflow</type>
<type>Other</type>
</types>
<!-- list, required -->
<authors>
<author>
<!-- string, required (@github, @symphony, @twitter optional) -->
<name github="username" symphony="username" twitter="username">Your Name</name>
<!-- string, optional. If used must validate as email -->
<email>name@domain.co.uk</email>
<!-- string, optional. If used must validate with http:// -->
<website>http://domain.co.uk</website>
</author>
</authors>
<!-- list, optional -->
<dependencies>
<dependency version="0.5.0">extension_path</dependency>
</dependencies>
<!-- list, required at least one. Largest version number is assumed latest -->
<releases>
<release version="0.3" date="2011-03-03" min="2.3" max="2.4"/>
<release version="0.2" date="2011-02-02" min="2.2.2">
<![CDATA[
Your release notes go in here. They will be parsed as Markdown.
* So you can have a list
* Or `code` if you like
]]>
</release>
<release version="0.1" date="2011-01-01" min="2.0"/>
</releases>
</extension>
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://symphony-cms.com/extensions/1.0"
xmlns="http://symphony-cms.com/extensions/1.0"
elementFormDefault="qualified">
<!-- defines a symphony extension ID -->
<xs:simpleType name="idType">
<xs:restriction base="xs:string">
<xs:pattern value="[a-z_]+" />
</xs:restriction>
</xs:simpleType>
<!-- defines an http url -->
<xs:simpleType name="httpType">
<xs:restriction base="xs:string">
<xs:pattern value="https?://(([a-z]+|[A-Z]+|[0-9]+|-+|_+)+\.)?([a-z]+|[A-Z]+|[0-9]+|-+|_+)+\.([a-z]+|[A-Z]+|[0-9]+|-+|_+)+(\.([a-z]+|[A-Z]+|[0-9]+|-+|_+)+)?((/([a-z]+|[A-Z]+|[0-9]+|-+|_+)+(/([a-z]+|[A-Z]+|[0-9]+|-+|_+)+)?)+)?" />
</xs:restriction>
</xs:simpleType>
<!-- defines an email address -->
<xs:simpleType name="emailType">
<xs:restriction base="xs:string">
<xs:pattern value="([a-z]|[A-Z]|[0-9]|-|_)+(([a-z]|[A-Z]|[0-9]|-|_|%)+)?@([a-z]|[A-Z]|[0-9]|-|_)+\.([a-z]|[A-Z]|[0-9]|-|_)+(\.([a-z]|[A-Z]|[0-9]|-|_)+)?" />
</xs:restriction>
</xs:simpleType>
<!-- defines an accepted repository type -->
<xs:simpleType name="repoType">
<xs:restriction base="xs:string">
<xs:pattern value="github" />
</xs:restriction>
</xs:simpleType>
<!-- defines an accepted url type -->
<xs:simpleType name="urlType">
<xs:restriction base="xs:string">
<xs:pattern value="homepage|wiki|other" />
</xs:restriction>
</xs:simpleType>
<!-- defines a symphony/extension version number -->
<xs:simpleType name="versionType">
<xs:restriction base="xs:string">
<xs:pattern value="\d\.\d(\.\d)?" />
</xs:restriction>
</xs:simpleType>
<!-- defines an extension type keyword -->
<xs:simpleType name="typeType">
<xs:restriction base="xs:string">
<xs:pattern value="Event|Field|Interface|Multimedia|Text Formatter|Translation|Workflow|Other" />
</xs:restriction>
</xs:simpleType>
<!-- defines a text boolean type of yes/no -->
<xs:simpleType name="boolType">
<xs:restriction base="xs:string">
<xs:pattern value="yes|no" />
</xs:restriction>
</xs:simpleType>
<!-- defines an extension description -->
<xs:complexType name="descriptionNode">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="lang" type="xs:string" use="optional" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<!-- defines an extension repository -->
<xs:complexType name="repoNode">
<xs:simpleContent>
<xs:extension base="httpType">
<xs:attribute name="type" type="repoType" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<!-- defines an extension resource url -->
<xs:complexType name="urlNode">
<xs:simpleContent>
<xs:extension base="httpType">
<xs:attribute name="type" type="urlType" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<!-- defines a set of extension types -->
<xs:complexType name="typesNode">
<xs:sequence>
<xs:element name="type" type="typeType" minOccurs="1" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
<!-- defines an author name -->
<xs:complexType name="authorNameNode">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="symphony" type="xs:string" use="required" />
<xs:attribute name="github" type="xs:string" use="optional" />
<xs:attribute name="twitter" type="xs:string" use="optional" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<!-- defines an extension author -->
<xs:complexType name="authorNode">
<xs:sequence>
<xs:element name="name" type="authorNameNode" minOccurs="1" maxOccurs="1" />
<xs:element name="email" type="emailType" minOccurs="0" maxOccurs="1" />
<xs:element name="website" type="httpType" minOccurs="0" maxOccurs="1" />
</xs:sequence>
</xs:complexType>
<!-- defines a set of extension authors -->
<xs:complexType name="authorsNode">
<xs:sequence>
<xs:element name="author" type="authorNode" minOccurs="1" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
<!-- defines an extension dependency -->
<xs:complexType name="dependencyNode">
<xs:simpleContent>
<xs:extension base="idType">
<xs:attribute name="version" type="versionType" use="required" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<!-- defines a set of extension dependencies -->
<xs:complexType name="dependenciesNode">
<xs:sequence>
<xs:element name="dependency" type="dependencyNode" minOccurs="1" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
<!-- defines an extension release -->
<xs:complexType name="releaseNode">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="version" type="versionType" use="required" />
<xs:attribute name="date" type="xs:date" use="required" />
<xs:attribute name="min" type="versionType" use="required" />
<xs:attribute name="max" type="versionType" use="optional" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<!-- defines a list of extension releases -->
<xs:complexType name="releasesNode">
<xs:sequence>
<xs:element name="release" type="releaseNode" minOccurs="1" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
<!-- defines an extension -->
<xs:complexType name="extensionNode">
<xs:sequence>
<xs:element name="name" type="xs:string" minOccurs="1" maxOccurs="1" />
<xs:element name="description" type="descriptionNode" minOccurs="1" maxOccurs="unbounded" />
<xs:element name="repo" type="repoNode" minOccurs="1" maxOccurs="1" />
<xs:element name="url" type="urlNode" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="types" type="typesNode" minOccurs="0" maxOccurs="1" />
<xs:element name="authors" type="authorsNode" minOccurs="1" maxOccurs="1" />
<xs:element name="dependencies" type="dependenciesNode" minOccurs="0" maxOccurs="1" />
<xs:element name="releases" type="releasesNode" minOccurs="1" maxOccurs="1" />
</xs:sequence>
<xs:attribute name="id" type="idType" use="required" />
</xs:complexType>
<!-- define the root node -->
<xs:element name="extension" type="extensionNode" />
</xs:schema>
<?php
class Validator
{
public $dom;
public $xsd;
public $errors;
public function __construct($xml=null, $xsd=null)
{
if(!is_null($xml) && !is_null($xsd))
{
$this->xsd = $xsd;
$this->dom = new DOMDocument();
$this->dom->load($xml);
}
}
/**
* Validate an XML document against a schema file
*/
public function validate()
{
libxml_use_internal_errors(true);
try
{
if(!$this->dom->schemaValidate($this->xsd))
{
$this->displayErrors();
}
}
catch(Exception $ex)
{
$this->errors[] = array(
'message' => $ex->getMessage()
);
}
libxml_use_internal_errors(false);
}
/**
* XML Validation single error display function.
*
* @return Array Error message array generated by failure to validate
*/
public function displayError($error)
{
$return = array();
switch($error->level)
{
case LIBXML_ERR_WARNING:
$return['level'] = 'Warning';
break;
case LIBXML_ERR_ERROR:
$return['level'] = 'Error';
break;
case LIBXML_ERR_FATAL:
$return['level'] = 'Fatal Error';
break;
}
$return['code'] = $error->code;
$return['message'] = trim($error->message);
if($error->file)
{
$return['file'] = $error->file;
}
$return['line'] = $error->line;
return $return;
}
/**
* XML Validation errors display function.
*
* @return String Error message generated by failure to validate
*/
public function displayErrors()
{
$errors = libxml_get_errors();
foreach($errors as $error)
{
$this->errors[] = $this->displayError($error);
}
var_dump($this->errors);
libxml_clear_errors();
}
}
$Validator = new Validator('extension.about.xml', 'extension.about.xsd');
$Validator->validate();
@designermonkey
Copy link
Author

Needs testing, especially the regex

@designermonkey
Copy link
Author

Updated the xml, xsd needs to reflect the change, after discussion

@designermonkey
Copy link
Author

Completed!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment