Signed-off-by: Joas Schilling <coding@schilljs.com>tags/v14.0.0beta1
@@ -44,20 +44,12 @@ use OC\App\CodeChecker\PrivateCheck; | |||
class CheckCode extends Command implements CompletionAwareInterface { | |||
/** @var InfoParser */ | |||
private $infoParser; | |||
protected $checkers = [ | |||
'private' => PrivateCheck::class, | |||
'deprecation' => DeprecationCheck::class, | |||
'strong-comparison' => StrongComparisonCheck::class, | |||
]; | |||
public function __construct(InfoParser $infoParser) { | |||
parent::__construct(); | |||
$this->infoParser = $infoParser; | |||
} | |||
protected function configure() { | |||
$this | |||
->setName('app:check-code') | |||
@@ -134,50 +126,12 @@ class CheckCode extends Command implements CompletionAwareInterface { | |||
} | |||
if(!$input->getOption('skip-validate-info')) { | |||
$infoChecker = new InfoChecker($this->infoParser); | |||
$infoChecker->listen('InfoChecker', 'mandatoryFieldMissing', function($key) use ($output) { | |||
$output->writeln("<error>Mandatory field missing: $key</error>"); | |||
}); | |||
$infoChecker->listen('InfoChecker', 'deprecatedFieldFound', function($key, $value) use ($output) { | |||
if($value === [] || is_null($value) || $value === '') { | |||
$output->writeln("<info>Deprecated field available: $key</info>"); | |||
} else { | |||
$output->writeln("<info>Deprecated field available: $key => $value</info>"); | |||
} | |||
}); | |||
$infoChecker->listen('InfoChecker', 'missingRequirement', function($minMax) use ($output) { | |||
$output->writeln("<error>Nextcloud $minMax version requirement missing</error>"); | |||
}); | |||
$infoChecker->listen('InfoChecker', 'differentVersions', function($versionFile, $infoXML) use ($output) { | |||
$output->writeln("<error>Different versions provided (appinfo/version: $versionFile - appinfo/info.xml: $infoXML)</error>"); | |||
// Can not inject because of circular dependency | |||
$infoChecker = new InfoChecker(\OC::$server->getAppManager()); | |||
$infoChecker->listen('InfoChecker', 'parseError', function($error) use ($output) { | |||
$output->writeln("<error>Invalid appinfo.xml file found: $error</error>"); | |||
}); | |||
$infoChecker->listen('InfoChecker', 'sameVersions', function($path) use ($output) { | |||
$output->writeln("<info>Version file isn't needed anymore and can be safely removed ($path)</info>"); | |||
}); | |||
$infoChecker->listen('InfoChecker', 'migrateVersion', function($version) use ($output) { | |||
$output->writeln("<error>Migrate the app version to appinfo/info.xml (add <version>$version</version> to appinfo/info.xml and remove appinfo/version)</error>"); | |||
}); | |||
if(OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { | |||
$infoChecker->listen('InfoChecker', 'mandatoryFieldFound', function($key, $value) use ($output) { | |||
$output->writeln("<info>Mandatory field available: $key => $value</info>"); | |||
}); | |||
$infoChecker->listen('InfoChecker', 'optionalFieldFound', function($key, $value) use ($output) { | |||
$output->writeln("<info>Optional field available: $key => $value</info>"); | |||
}); | |||
$infoChecker->listen('InfoChecker', 'unusedFieldFound', function($key, $value) use ($output) { | |||
$output->writeln("<info>Unused field available: $key => $value</info>"); | |||
}); | |||
} | |||
$infoErrors = $infoChecker->analyse($appId); | |||
$errors = array_merge($errors, $infoErrors); |
@@ -40,8 +40,7 @@ | |||
$application->add(new \Stecman\Component\Symfony\Console\BashCompletion\CompletionCommand()); | |||
$application->add(new OC\Core\Command\Status); | |||
$application->add(new OC\Core\Command\Check(\OC::$server->getSystemConfig())); | |||
$infoParser = new \OC\App\InfoParser(); | |||
$application->add(new OC\Core\Command\App\CheckCode($infoParser)); | |||
$application->add(new OC\Core\Command\App\CheckCode()); | |||
$application->add(new OC\Core\Command\L10n\CreateJs()); | |||
$application->add(new \OC\Core\Command\Integrity\SignApp( | |||
\OC::$server->getIntegrityCodeChecker(), |
@@ -411,6 +411,7 @@ class AppManager implements IAppManager { | |||
/** | |||
* @inheritdoc | |||
* In case you change this method, also change \OC\App\CodeChecker\InfoChecker::isShipped() | |||
*/ | |||
public function isShipped($appId) { | |||
$this->loadShippedJson(); | |||
@@ -422,6 +423,10 @@ class AppManager implements IAppManager { | |||
return in_array($appId, $alwaysEnabled, true); | |||
} | |||
/** | |||
* In case you change this method, also change \OC\App\CodeChecker\InfoChecker::loadShippedJson() | |||
* @throws \Exception | |||
*/ | |||
private function loadShippedJson() { | |||
if ($this->shippedApps === null) { | |||
$shippedJson = \OC::$SERVERROOT . '/core/shipped.json'; |
@@ -23,121 +23,88 @@ | |||
namespace OC\App\CodeChecker; | |||
use OC\App\InfoParser; | |||
use OC\Hooks\BasicEmitter; | |||
use OCP\App\AppPathNotFoundException; | |||
use OCP\App\IAppManager; | |||
class InfoChecker extends BasicEmitter { | |||
/** @var InfoParser */ | |||
private $infoParser; | |||
/** @var string[] */ | |||
private $shippedApps; | |||
private $mandatoryFields = [ | |||
'author', | |||
'description', | |||
'dependencies', | |||
'id', | |||
'licence', | |||
'name', | |||
'version', | |||
]; | |||
private $optionalFields = [ | |||
'bugs', | |||
'category', | |||
'default_enable', | |||
'documentation', | |||
'namespace', | |||
'ocsid', | |||
'public', | |||
'remote', | |||
'repository', | |||
'types', | |||
'website', | |||
]; | |||
private $deprecatedFields = [ | |||
'info', | |||
'require', | |||
'requiremax', | |||
'requiremin', | |||
'shipped', | |||
'standalone', | |||
]; | |||
public function __construct(InfoParser $infoParser) { | |||
$this->infoParser = $infoParser; | |||
} | |||
/** @var string[] */ | |||
private $alwaysEnabled; | |||
/** | |||
* @param string $appId | |||
* @return array | |||
* @throws \RuntimeException | |||
*/ | |||
public function analyse($appId) { | |||
public function analyse($appId): array { | |||
$appPath = \OC_App::getAppPath($appId); | |||
if ($appPath === false) { | |||
throw new \RuntimeException("No app with given id <$appId> known."); | |||
} | |||
$errors = []; | |||
$info = $this->infoParser->parse($appPath . '/appinfo/info.xml'); | |||
if (!isset($info['dependencies']['nextcloud']['@attributes']['min-version'])) { | |||
$errors[] = [ | |||
'type' => 'missingRequirement', | |||
'field' => 'min', | |||
]; | |||
$this->emit('InfoChecker', 'missingRequirement', ['min']); | |||
} | |||
if (!isset($info['dependencies']['nextcloud']['@attributes']['max-version'])) { | |||
$errors[] = [ | |||
'type' => 'missingRequirement', | |||
'field' => 'max', | |||
]; | |||
$this->emit('InfoChecker', 'missingRequirement', ['max']); | |||
} | |||
foreach ($info as $key => $value) { | |||
if(is_array($value)) { | |||
$value = json_encode($value); | |||
} | |||
if (in_array($key, $this->mandatoryFields)) { | |||
$this->emit('InfoChecker', 'mandatoryFieldFound', [$key, $value]); | |||
continue; | |||
} | |||
$xml = new \DOMDocument(); | |||
$xml->load($appPath . '/appinfo/info.xml'); | |||
if (in_array($key, $this->optionalFields)) { | |||
$this->emit('InfoChecker', 'optionalFieldFound', [$key, $value]); | |||
continue; | |||
$schema = \OC::$SERVERROOT . '/resources/app-info.xsd'; | |||
try { | |||
if ($this->isShipped($appId)) { | |||
// Shipped apps are allowed to have the public and default_enabled tags | |||
$schema = \OC::$SERVERROOT . '/resources/app-info-shipped.xsd'; | |||
} | |||
if (in_array($key, $this->deprecatedFields)) { | |||
// skip empty arrays - empty arrays for remote and public are always added | |||
if($value === '[]' && in_array($key, ['public', 'remote', 'info'])) { | |||
continue; | |||
} | |||
$this->emit('InfoChecker', 'deprecatedFieldFound', [$key, $value]); | |||
continue; | |||
} | |||
$this->emit('InfoChecker', 'unusedFieldFound', [$key, $value]); | |||
} catch (\Exception $e) { | |||
// Assume it is not shipped | |||
} | |||
foreach ($this->mandatoryFields as $key) { | |||
if(!isset($info[$key])) { | |||
$this->emit('InfoChecker', 'mandatoryFieldMissing', [$key]); | |||
$errors = []; | |||
if (!$xml->schemaValidate($schema)) { | |||
foreach (libxml_get_errors() as $error) { | |||
$errors[] = [ | |||
'type' => 'mandatoryFieldMissing', | |||
'field' => $key, | |||
'type' => 'parseError', | |||
'field' => $error->message, | |||
]; | |||
$this->emit('InfoChecker', 'parseError', [$error->message]); | |||
} | |||
} | |||
$versionFile = $appPath . '/appinfo/version'; | |||
if (is_file($versionFile)) { | |||
$version = trim(file_get_contents($versionFile)); | |||
$this->emit('InfoChecker', 'migrateVersion', [$version]); | |||
} | |||
return $errors; | |||
} | |||
/** | |||
* This is a copy of \OC\App\AppManager::isShipped(), keep both in sync. | |||
* This method is copied, so the code checker works even when Nextcloud is | |||
* not installed yet. The AppManager requires a database connection, which | |||
* fails in that case. | |||
* | |||
* @param string $appId | |||
* @return bool | |||
* @throws \Exception | |||
*/ | |||
protected function isShipped(string $appId): bool { | |||
$this->loadShippedJson(); | |||
return \in_array($appId, $this->shippedApps, true); | |||
} | |||
/** | |||
* This is a copy of \OC\App\AppManager::loadShippedJson(), keep both in sync | |||
* This method is copied, so the code checker works even when Nextcloud is | |||
* not installed yet. The AppManager requires a database connection, which | |||
* fails in that case. | |||
* | |||
* @throws \Exception | |||
*/ | |||
protected function loadShippedJson() { | |||
if ($this->shippedApps === null) { | |||
$shippedJson = \OC::$SERVERROOT . '/core/shipped.json'; | |||
if (!file_exists($shippedJson)) { | |||
throw new \Exception("File not found: $shippedJson"); | |||
} | |||
$content = json_decode(file_get_contents($shippedJson), true); | |||
$this->shippedApps = $content['shippedApps']; | |||
$this->alwaysEnabled = $content['alwaysEnabled']; | |||
} | |||
} | |||
} |
@@ -0,0 +1,671 @@ | |||
<?xml version="1.0"?> | |||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" | |||
elementFormDefault="qualified" attributeFormDefault="unqualified"> | |||
<xs:element name="info"> | |||
<xs:complexType> | |||
<xs:sequence> | |||
<xs:element name="id" type="id" minOccurs="1" maxOccurs="1"/> | |||
<xs:element name="name" type="l10n-string" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
<xs:element name="summary" type="l10n-string" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
<xs:element name="description" type="l10n-text" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
<xs:element name="version" type="semver" | |||
minOccurs="1" maxOccurs="1"/> | |||
<xs:element name="licence" type="licence" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
<xs:element name="author" type="author" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
<xs:element name="namespace" type="limited-string" | |||
minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="default_enable" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="types" type="types" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="documentation" type="documentation" | |||
minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="category" type="category" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
<xs:element name="website" type="url" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="discussion" type="url" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="bugs" type="url" minOccurs="1" | |||
maxOccurs="1"/> | |||
<xs:element name="repository" type="repository" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="screenshot" type="screenshot" minOccurs="0" | |||
maxOccurs="10"/> | |||
<xs:element name="dependencies" type="dependencies" | |||
minOccurs="1" maxOccurs="1"/> | |||
<xs:element name="background-jobs" type="jobs" | |||
minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="repair-steps" type="repair-steps" | |||
minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="two-factor-providers" | |||
type="two-factor-providers" | |||
minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="commands" type="commands" | |||
minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="settings" type="settings" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="activity" type="activity" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="navigations" type="navigations" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="contactsmenu" type="contactsmenu" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="collaboration" type="collaboration" minOccurs="0" | |||
maxOccurs="1" /> | |||
<xs:element name="sabre" type="sabre" minOccurs="0" | |||
maxOccurs="1" /> | |||
<xs:element name="public" type="public" minOccurs="0" | |||
maxOccurs="1" /> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:unique name="uniqueNameL10n"> | |||
<xs:selector xpath="name"/> | |||
<xs:field xpath="@lang"/> | |||
</xs:unique> | |||
<xs:unique name="uniqueSummaryL10n"> | |||
<xs:selector xpath="summary"/> | |||
<xs:field xpath="@lang"/> | |||
</xs:unique> | |||
<xs:unique name="uniqueDescriptionL10n"> | |||
<xs:selector xpath="description"/> | |||
<xs:field xpath="@lang"/> | |||
</xs:unique> | |||
<xs:unique name="uniqueLicense"> | |||
<xs:selector xpath="licence"/> | |||
<xs:field xpath="."/> | |||
</xs:unique> | |||
<xs:unique name="uniqueTypes"> | |||
<xs:selector xpath="types/type"/> | |||
<xs:field xpath="."/> | |||
</xs:unique> | |||
<xs:unique name="uniqueCategory"> | |||
<xs:selector xpath="category"/> | |||
<xs:field xpath="."/> | |||
</xs:unique> | |||
<xs:unique name="uniqueDatabase"> | |||
<xs:selector xpath="dependencies/database"/> | |||
<xs:field xpath="."/> | |||
</xs:unique> | |||
<xs:unique name="uniqueLib"> | |||
<xs:selector xpath="dependencies/lib"/> | |||
<xs:field xpath="."/> | |||
</xs:unique> | |||
<xs:unique name="uniqueCommand"> | |||
<xs:selector xpath="dependencies/command"/> | |||
<xs:field xpath="."/> | |||
</xs:unique> | |||
</xs:element> | |||
<!-- basic types --> | |||
<xs:simpleType name="empty-string"> | |||
<xs:restriction base="xs:string"> | |||
<xs:maxLength value="0"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="non-empty-string"> | |||
<xs:restriction base="xs:string"> | |||
<xs:minLength value="1"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="limited-string"> | |||
<xs:restriction base="non-empty-string"> | |||
<xs:maxLength value="256"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:complexType name="l10n-text"> | |||
<xs:simpleContent> | |||
<xs:extension base="non-empty-string"> | |||
<xs:attribute name="lang" type="l10n-code" default="en" | |||
use="optional"/> | |||
</xs:extension> | |||
</xs:simpleContent> | |||
</xs:complexType> | |||
<xs:complexType name="l10n-string"> | |||
<xs:simpleContent> | |||
<xs:restriction base="l10n-text"> | |||
<xs:maxLength value="128"/> | |||
</xs:restriction> | |||
</xs:simpleContent> | |||
</xs:complexType> | |||
<xs:simpleType name="l10n-code"> | |||
<xs:restriction base="xs:string"> | |||
<xs:enumeration value="af"/> | |||
<xs:enumeration value="ar"/> | |||
<xs:enumeration value="ast"/> | |||
<xs:enumeration value="az"/> | |||
<xs:enumeration value="bg"/> | |||
<xs:enumeration value="be"/> | |||
<xs:enumeration value="bn"/> | |||
<xs:enumeration value="br"/> | |||
<xs:enumeration value="bs"/> | |||
<xs:enumeration value="ca"/> | |||
<xs:enumeration value="cs"/> | |||
<xs:enumeration value="cy"/> | |||
<xs:enumeration value="da"/> | |||
<xs:enumeration value="de"/> | |||
<xs:enumeration value="el"/> | |||
<xs:enumeration value="en"/> | |||
<xs:enumeration value="eo"/> | |||
<xs:enumeration value="es"/> | |||
<xs:enumeration value="es-ar"/> | |||
<xs:enumeration value="es-co"/> | |||
<xs:enumeration value="es-mx"/> | |||
<xs:enumeration value="es-ni"/> | |||
<xs:enumeration value="es-ve"/> | |||
<xs:enumeration value="et"/> | |||
<xs:enumeration value="eu"/> | |||
<xs:enumeration value="fa"/> | |||
<xs:enumeration value="fi"/> | |||
<xs:enumeration value="fr"/> | |||
<xs:enumeration value="fy"/> | |||
<xs:enumeration value="ga"/> | |||
<xs:enumeration value="gd"/> | |||
<xs:enumeration value="gl"/> | |||
<xs:enumeration value="he"/> | |||
<xs:enumeration value="hi"/> | |||
<xs:enumeration value="hr"/> | |||
<xs:enumeration value="hu"/> | |||
<xs:enumeration value="ia"/> | |||
<xs:enumeration value="id"/> | |||
<xs:enumeration value="io"/> | |||
<xs:enumeration value="is"/> | |||
<xs:enumeration value="it"/> | |||
<xs:enumeration value="ja"/> | |||
<xs:enumeration value="ka"/> | |||
<xs:enumeration value="kk"/> | |||
<xs:enumeration value="km"/> | |||
<xs:enumeration value="kn"/> | |||
<xs:enumeration value="ko"/> | |||
<xs:enumeration value="lb"/> | |||
<xs:enumeration value="lt"/> | |||
<xs:enumeration value="lv"/> | |||
<xs:enumeration value="mk"/> | |||
<xs:enumeration value="ml"/> | |||
<xs:enumeration value="mn"/> | |||
<xs:enumeration value="mr"/> | |||
<xs:enumeration value="my"/> | |||
<xs:enumeration value="nb"/> | |||
<xs:enumeration value="ne"/> | |||
<xs:enumeration value="nl"/> | |||
<xs:enumeration value="nn"/> | |||
<xs:enumeration value="os"/> | |||
<xs:enumeration value="pa"/> | |||
<xs:enumeration value="pl"/> | |||
<xs:enumeration value="pt"/> | |||
<xs:enumeration value="pt-br"/> | |||
<xs:enumeration value="ro"/> | |||
<xs:enumeration value="ru"/> | |||
<xs:enumeration value="sk"/> | |||
<xs:enumeration value="sl"/> | |||
<xs:enumeration value="sq"/> | |||
<xs:enumeration value="sr"/> | |||
<xs:enumeration value="sr-latn"/> | |||
<xs:enumeration value="sv"/> | |||
<xs:enumeration value="sw"/> | |||
<xs:enumeration value="ta"/> | |||
<xs:enumeration value="te"/> | |||
<xs:enumeration value="th"/> | |||
<xs:enumeration value="tr"/> | |||
<xs:enumeration value="tt"/> | |||
<xs:enumeration value="udm"/> | |||
<xs:enumeration value="uk"/> | |||
<xs:enumeration value="ur"/> | |||
<xs:enumeration value="vi"/> | |||
<xs:enumeration value="zh-hans"/> | |||
<xs:enumeration value="zh-hant"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="semver"> | |||
<xs:restriction base="limited-string"> | |||
<xs:pattern | |||
value="(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(\-([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="version"> | |||
<xs:restriction base="limited-string"> | |||
<xs:pattern value="[0-9]+(\.[0-9]+){0,2}"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="url"> | |||
<xs:restriction base="xs:anyURI"> | |||
<xs:pattern value="https?://.+"/> | |||
<xs:maxLength value="256"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="doc-user-url"> | |||
<xs:restriction base="non-empty-string"> | |||
<xs:pattern value="https://.+|user-[a-z]+[\-a-z]*[a-z]+"/> | |||
<xs:maxLength value="256"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="doc-admin-url"> | |||
<xs:restriction base="non-empty-string"> | |||
<xs:pattern value="https://.+|admin-[a-z]+[\-a-z]*[a-z]+"/> | |||
<xs:maxLength value="256"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="doc-developer-url"> | |||
<xs:restriction base="non-empty-string"> | |||
<xs:pattern value="https://.+|developer-[a-z]+[\-a-z]*[a-z]+"/> | |||
<xs:maxLength value="256"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="secure-url"> | |||
<xs:restriction base="xs:anyURI"> | |||
<xs:pattern value="https://.+"/> | |||
<xs:maxLength value="256"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="email"> | |||
<xs:restriction base="limited-string"> | |||
<xs:pattern value="[^@]+@[^\.]+\..+"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<!-- first level elements --> | |||
<xs:complexType name="screenshot"> | |||
<xs:simpleContent> | |||
<xs:extension base="secure-url"> | |||
<xs:attribute name="small-thumbnail" use="optional" | |||
type="secure-url"/> | |||
</xs:extension> | |||
</xs:simpleContent> | |||
</xs:complexType> | |||
<xs:simpleType name="id"> | |||
<xs:restriction base="limited-string"> | |||
<xs:pattern value="[a-z]+[a-z0-9_]*[a-z0-9]+"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:complexType name="author"> | |||
<xs:simpleContent> | |||
<xs:extension base="limited-string"> | |||
<xs:attribute name="mail" type="email" use="optional"/> | |||
<xs:attribute name="homepage" type="url" use="optional"/> | |||
</xs:extension> | |||
</xs:simpleContent> | |||
</xs:complexType> | |||
<xs:complexType name="repository"> | |||
<xs:simpleContent> | |||
<xs:extension base="url"> | |||
<xs:attribute name="type" type="vcs" use="optional" | |||
default="git"/> | |||
</xs:extension> | |||
</xs:simpleContent> | |||
</xs:complexType> | |||
<xs:simpleType name="vcs"> | |||
<xs:restriction base="xs:string"> | |||
<xs:enumeration value="git"/> | |||
<xs:enumeration value="mercurial"/> | |||
<xs:enumeration value="subversion"/> | |||
<xs:enumeration value="bzr"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:complexType name="types"> | |||
<xs:sequence> | |||
<xs:element name="prelogin" minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="filesystem" minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="authentication" minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="logging" minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="dav" minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="prevent_group_restriction" minOccurs="0" | |||
maxOccurs="1"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:simpleType name="category"> | |||
<xs:restriction base="xs:string"> | |||
<xs:enumeration value="security"/> | |||
<xs:enumeration value="customization"/> | |||
<xs:enumeration value="files"/> | |||
<xs:enumeration value="integration"/> | |||
<xs:enumeration value="monitoring"/> | |||
<xs:enumeration value="multimedia"/> | |||
<xs:enumeration value="office"/> | |||
<xs:enumeration value="organization"/> | |||
<xs:enumeration value="social"/> | |||
<xs:enumeration value="tools"/> | |||
<xs:enumeration value="games"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="licence"> | |||
<xs:restriction base="xs:string"> | |||
<xs:enumeration value="agpl"/> | |||
<xs:enumeration value="mpl"/> | |||
<xs:enumeration value="apache"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="databases"> | |||
<xs:restriction base="xs:string"> | |||
<xs:enumeration value="sqlite"/> | |||
<xs:enumeration value="mysql"/> | |||
<xs:enumeration value="pgsql"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:complexType name="documentation"> | |||
<xs:sequence> | |||
<xs:element name="user" type="doc-user-url" minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="admin" type="doc-admin-url" minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="developer" type="doc-developer-url" minOccurs="0" | |||
maxOccurs="1"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="settings"> | |||
<xs:sequence> | |||
<xs:element name="admin" type="php-class" minOccurs="0" | |||
maxOccurs="unbounded"/> | |||
<xs:element name="admin-section" type="php-class" minOccurs="0" | |||
maxOccurs="unbounded"/> | |||
<xs:element name="personal" type="php-class" minOccurs="0" | |||
maxOccurs="unbounded"/> | |||
<xs:element name="personal-section" type="php-class" minOccurs="0" | |||
maxOccurs="unbounded"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="activity"> | |||
<xs:sequence> | |||
<xs:element name="settings" type="activity-settings" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="filters" type="activity-filters" minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="providers" type="activity-providers" minOccurs="0" | |||
maxOccurs="1"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="activity-settings"> | |||
<xs:sequence> | |||
<xs:element name="setting" type="php-class" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="activity-filters"> | |||
<xs:sequence> | |||
<xs:element name="filter" type="php-class" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="activity-providers"> | |||
<xs:sequence> | |||
<xs:element name="provider" type="php-class" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="navigations"> | |||
<xs:sequence> | |||
<xs:element name="navigation" type="navigation" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="navigation"> | |||
<xs:sequence> | |||
<xs:element name="id" type="id" minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="name" type="non-empty-string" minOccurs="1" maxOccurs="1"/> | |||
<xs:element name="route" type="route" minOccurs="1" maxOccurs="1"/> | |||
<xs:element name="icon" type="xs:anyURI" minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="order" type="xs:int" minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="type" type="navigation-type" minOccurs="0" maxOccurs="1"/> | |||
</xs:sequence> | |||
<xs:attribute name="role" type="navigation-role" default="all" use="optional"/> | |||
</xs:complexType> | |||
<xs:simpleType name="navigation-role"> | |||
<xs:restriction base="xs:string"> | |||
<xs:enumeration value="all"/> | |||
<xs:enumeration value="admin"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="navigation-type"> | |||
<xs:restriction base="xs:string"> | |||
<xs:enumeration value="link"/> | |||
<xs:enumeration value="settings"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="route"> | |||
<xs:restriction base="non-empty-string"> | |||
<xs:pattern value="[0-9a-zA-Z_]+(\.[0-9a-zA-Z_]+){2}"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:complexType name="contactsmenu"> | |||
<xs:sequence> | |||
<xs:element name="provider" type="php-class" minOccurs="1" | |||
maxOccurs="1"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="collaboration"> | |||
<xs:sequence> | |||
<xs:element name="plugins" type="collaboration-plugins" minOccurs="0" maxOccurs="1"> | |||
</xs:element> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="collaboration-plugins"> | |||
<xs:sequence> | |||
<xs:element name="plugin" type="collaboration-plugins-plugin" minOccurs="1" maxOccurs="unbounded"> | |||
</xs:element> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="collaboration-plugins-plugin"> | |||
<xs:simpleContent> | |||
<xs:extension base="php-class"> | |||
<xs:attribute name="type" type="collaboration-plugin-type" use="required"/> | |||
<xs:attribute name="share-type" type="share-type" /> | |||
</xs:extension> | |||
</xs:simpleContent> | |||
</xs:complexType> | |||
<xs:simpleType name="collaboration-plugin-type"> | |||
<xs:restriction base="xs:string"> | |||
<xs:enumeration value="collaborator-search"/> | |||
<xs:enumeration value="autocomplete-sort"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:complexType name="sabre"> | |||
<xs:sequence> | |||
<xs:element name="collections" type="sabre-collections" minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="plugins" type="sabre-plugins" minOccurs="0" maxOccurs="1"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="sabre-collections"> | |||
<xs:sequence> | |||
<xs:element name="collection" type="php-class" minOccurs="1" maxOccurs="unbounded"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="sabre-plugins"> | |||
<xs:sequence> | |||
<xs:element name="plugin" type="php-class" minOccurs="1" maxOccurs="unbounded"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:simpleType name="share-type"> | |||
<xs:restriction base="xs:string"> | |||
<xs:enumeration value="SHARE_TYPE_USER"/> | |||
<xs:enumeration value="SHARE_TYPE_GROUP"/> | |||
<xs:enumeration value="SHARE_TYPE_LINK"/> | |||
<xs:enumeration value="SHARE_TYPE_EMAIL"/> | |||
<xs:enumeration value="SHARE_TYPE_CONTACT"/> | |||
<xs:enumeration value="SHARE_TYPE_REMOTE"/> | |||
<xs:enumeration value="SHARE_TYPE_CIRCLE"/> | |||
<xs:enumeration value="SHARE_TYPE_GUEST"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:complexType name="public"> | |||
<xs:sequence> | |||
<xs:element name="webdav" type="path" minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="files" type="path" minOccurs="0" maxOccurs="1"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<!-- dependencies --> | |||
<xs:complexType name="dependencies"> | |||
<xs:sequence> | |||
<xs:element name="php" type="php" minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="database" type="database" minOccurs="0" | |||
maxOccurs="unbounded"/> | |||
<xs:element name="command" type="shell-command" minOccurs="0" | |||
maxOccurs="unbounded"/> | |||
<xs:element name="lib" type="min-max-version" minOccurs="0" | |||
maxOccurs="unbounded"/> | |||
<xs:element name="owncloud" type="owncloud" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="nextcloud" type="nextcloud" minOccurs="1" | |||
maxOccurs="1"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="owncloud"> | |||
<xs:attribute name="min-version" type="version" use="required"/> | |||
<xs:attribute name="max-version" type="version" use="optional"/> | |||
</xs:complexType> | |||
<xs:complexType name="nextcloud"> | |||
<xs:attribute name="min-version" type="version" use="required"/> | |||
<xs:attribute name="max-version" type="version" use="required"/> | |||
</xs:complexType> | |||
<xs:simpleType name="shell-command"> | |||
<xs:restriction base="limited-string"> | |||
<xs:pattern value="[a-zA-Z\-_]+"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:complexType name="min-max-version"> | |||
<xs:simpleContent> | |||
<xs:extension base="limited-string"> | |||
<xs:attribute name="min-version" type="version" | |||
use="optional"/> | |||
<xs:attribute name="max-version" type="version" | |||
use="optional"/> | |||
</xs:extension> | |||
</xs:simpleContent> | |||
</xs:complexType> | |||
<xs:complexType name="database"> | |||
<xs:simpleContent> | |||
<xs:extension base="databases"> | |||
<xs:attribute name="min-version" type="version" | |||
use="optional"/> | |||
<xs:attribute name="max-version" type="version" | |||
use="optional"/> | |||
</xs:extension> | |||
</xs:simpleContent> | |||
</xs:complexType> | |||
<xs:complexType name="php"> | |||
<xs:simpleContent> | |||
<xs:extension base="empty-string"> | |||
<xs:attribute name="min-int-size" type="bits" use="optional" | |||
default="32"/> | |||
<xs:attribute name="min-version" type="version" | |||
use="optional"/> | |||
<xs:attribute name="max-version" type="version" | |||
use="optional"/> | |||
</xs:extension> | |||
</xs:simpleContent> | |||
</xs:complexType> | |||
<xs:simpleType name="bits"> | |||
<xs:restriction base="xs:int"> | |||
<xs:enumeration value="32"/> | |||
<xs:enumeration value="64"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:complexType name="repair-steps"> | |||
<xs:sequence> | |||
<xs:element name="pre-migration" type="steps" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="post-migration" type="steps" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="live-migration" type="steps" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="install" type="steps" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="uninstall" type="steps" minOccurs="0" | |||
maxOccurs="1"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="jobs"> | |||
<xs:sequence> | |||
<xs:element name="job" type="php-class" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="steps"> | |||
<xs:sequence> | |||
<xs:element name="step" type="php-class" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="two-factor-providers"> | |||
<xs:sequence> | |||
<xs:element name="provider" type="php-class" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="commands"> | |||
<xs:sequence> | |||
<xs:element name="command" type="php-class" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:simpleType name="php-class"> | |||
<xs:restriction base="xs:string"> | |||
<xs:pattern | |||
value="[a-zA-Z_][0-9a-zA-Z_]*(\\[a-zA-Z_][0-9a-zA-Z_]*)*"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="path"> | |||
<xs:restriction base="xs:string"> | |||
<xs:pattern | |||
value="[a-zA-Z_][0-9a-zA-Z_]*(/[a-zA-Z_][0-9a-zA-Z_]*)*\.php"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
</xs:schema> |
@@ -0,0 +1,652 @@ | |||
<?xml version="1.0"?> | |||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" | |||
elementFormDefault="qualified" attributeFormDefault="unqualified"> | |||
<xs:element name="info"> | |||
<xs:complexType> | |||
<xs:sequence> | |||
<xs:element name="id" type="id" minOccurs="1" maxOccurs="1"/> | |||
<xs:element name="name" type="l10n-string" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
<xs:element name="summary" type="l10n-string" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
<xs:element name="description" type="l10n-text" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
<xs:element name="version" type="semver" | |||
minOccurs="1" maxOccurs="1"/> | |||
<xs:element name="licence" type="licence" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
<xs:element name="author" type="author" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
<xs:element name="namespace" type="limited-string" | |||
minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="types" type="types" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="documentation" type="documentation" | |||
minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="category" type="category" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
<xs:element name="website" type="url" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="discussion" type="url" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="bugs" type="url" minOccurs="1" | |||
maxOccurs="1"/> | |||
<xs:element name="repository" type="repository" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="screenshot" type="screenshot" minOccurs="0" | |||
maxOccurs="10"/> | |||
<xs:element name="dependencies" type="dependencies" | |||
minOccurs="1" maxOccurs="1"/> | |||
<xs:element name="background-jobs" type="jobs" | |||
minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="repair-steps" type="repair-steps" | |||
minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="two-factor-providers" | |||
type="two-factor-providers" | |||
minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="commands" type="commands" | |||
minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="settings" type="settings" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="activity" type="activity" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="navigations" type="navigations" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="contactsmenu" type="contactsmenu" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="collaboration" type="collaboration" minOccurs="0" | |||
maxOccurs="1" /> | |||
<xs:element name="sabre" type="sabre" minOccurs="0" | |||
maxOccurs="1" /> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:unique name="uniqueNameL10n"> | |||
<xs:selector xpath="name"/> | |||
<xs:field xpath="@lang"/> | |||
</xs:unique> | |||
<xs:unique name="uniqueSummaryL10n"> | |||
<xs:selector xpath="summary"/> | |||
<xs:field xpath="@lang"/> | |||
</xs:unique> | |||
<xs:unique name="uniqueDescriptionL10n"> | |||
<xs:selector xpath="description"/> | |||
<xs:field xpath="@lang"/> | |||
</xs:unique> | |||
<xs:unique name="uniqueLicense"> | |||
<xs:selector xpath="licence"/> | |||
<xs:field xpath="."/> | |||
</xs:unique> | |||
<xs:unique name="uniqueTypes"> | |||
<xs:selector xpath="types/type"/> | |||
<xs:field xpath="."/> | |||
</xs:unique> | |||
<xs:unique name="uniqueCategory"> | |||
<xs:selector xpath="category"/> | |||
<xs:field xpath="."/> | |||
</xs:unique> | |||
<xs:unique name="uniqueDatabase"> | |||
<xs:selector xpath="dependencies/database"/> | |||
<xs:field xpath="."/> | |||
</xs:unique> | |||
<xs:unique name="uniqueLib"> | |||
<xs:selector xpath="dependencies/lib"/> | |||
<xs:field xpath="."/> | |||
</xs:unique> | |||
<xs:unique name="uniqueCommand"> | |||
<xs:selector xpath="dependencies/command"/> | |||
<xs:field xpath="."/> | |||
</xs:unique> | |||
</xs:element> | |||
<!-- basic types --> | |||
<xs:simpleType name="empty-string"> | |||
<xs:restriction base="xs:string"> | |||
<xs:maxLength value="0"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="non-empty-string"> | |||
<xs:restriction base="xs:string"> | |||
<xs:minLength value="1"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="limited-string"> | |||
<xs:restriction base="non-empty-string"> | |||
<xs:maxLength value="256"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:complexType name="l10n-text"> | |||
<xs:simpleContent> | |||
<xs:extension base="non-empty-string"> | |||
<xs:attribute name="lang" type="l10n-code" default="en" | |||
use="optional"/> | |||
</xs:extension> | |||
</xs:simpleContent> | |||
</xs:complexType> | |||
<xs:complexType name="l10n-string"> | |||
<xs:simpleContent> | |||
<xs:restriction base="l10n-text"> | |||
<xs:maxLength value="128"/> | |||
</xs:restriction> | |||
</xs:simpleContent> | |||
</xs:complexType> | |||
<xs:simpleType name="l10n-code"> | |||
<xs:restriction base="xs:string"> | |||
<xs:enumeration value="af"/> | |||
<xs:enumeration value="ar"/> | |||
<xs:enumeration value="ast"/> | |||
<xs:enumeration value="az"/> | |||
<xs:enumeration value="bg"/> | |||
<xs:enumeration value="be"/> | |||
<xs:enumeration value="bn"/> | |||
<xs:enumeration value="br"/> | |||
<xs:enumeration value="bs"/> | |||
<xs:enumeration value="ca"/> | |||
<xs:enumeration value="cs"/> | |||
<xs:enumeration value="cy"/> | |||
<xs:enumeration value="da"/> | |||
<xs:enumeration value="de"/> | |||
<xs:enumeration value="el"/> | |||
<xs:enumeration value="en"/> | |||
<xs:enumeration value="eo"/> | |||
<xs:enumeration value="es"/> | |||
<xs:enumeration value="es-ar"/> | |||
<xs:enumeration value="es-co"/> | |||
<xs:enumeration value="es-mx"/> | |||
<xs:enumeration value="es-ni"/> | |||
<xs:enumeration value="es-ve"/> | |||
<xs:enumeration value="et"/> | |||
<xs:enumeration value="eu"/> | |||
<xs:enumeration value="fa"/> | |||
<xs:enumeration value="fi"/> | |||
<xs:enumeration value="fr"/> | |||
<xs:enumeration value="fy"/> | |||
<xs:enumeration value="ga"/> | |||
<xs:enumeration value="gd"/> | |||
<xs:enumeration value="gl"/> | |||
<xs:enumeration value="he"/> | |||
<xs:enumeration value="hi"/> | |||
<xs:enumeration value="hr"/> | |||
<xs:enumeration value="hu"/> | |||
<xs:enumeration value="ia"/> | |||
<xs:enumeration value="id"/> | |||
<xs:enumeration value="io"/> | |||
<xs:enumeration value="is"/> | |||
<xs:enumeration value="it"/> | |||
<xs:enumeration value="ja"/> | |||
<xs:enumeration value="ka"/> | |||
<xs:enumeration value="kk"/> | |||
<xs:enumeration value="km"/> | |||
<xs:enumeration value="kn"/> | |||
<xs:enumeration value="ko"/> | |||
<xs:enumeration value="lb"/> | |||
<xs:enumeration value="lt"/> | |||
<xs:enumeration value="lv"/> | |||
<xs:enumeration value="mk"/> | |||
<xs:enumeration value="ml"/> | |||
<xs:enumeration value="mn"/> | |||
<xs:enumeration value="mr"/> | |||
<xs:enumeration value="my"/> | |||
<xs:enumeration value="nb"/> | |||
<xs:enumeration value="ne"/> | |||
<xs:enumeration value="nl"/> | |||
<xs:enumeration value="nn"/> | |||
<xs:enumeration value="os"/> | |||
<xs:enumeration value="pa"/> | |||
<xs:enumeration value="pl"/> | |||
<xs:enumeration value="pt"/> | |||
<xs:enumeration value="pt-br"/> | |||
<xs:enumeration value="ro"/> | |||
<xs:enumeration value="ru"/> | |||
<xs:enumeration value="sk"/> | |||
<xs:enumeration value="sl"/> | |||
<xs:enumeration value="sq"/> | |||
<xs:enumeration value="sr"/> | |||
<xs:enumeration value="sr-latn"/> | |||
<xs:enumeration value="sv"/> | |||
<xs:enumeration value="sw"/> | |||
<xs:enumeration value="ta"/> | |||
<xs:enumeration value="te"/> | |||
<xs:enumeration value="th"/> | |||
<xs:enumeration value="tr"/> | |||
<xs:enumeration value="tt"/> | |||
<xs:enumeration value="udm"/> | |||
<xs:enumeration value="uk"/> | |||
<xs:enumeration value="ur"/> | |||
<xs:enumeration value="vi"/> | |||
<xs:enumeration value="zh-hans"/> | |||
<xs:enumeration value="zh-hant"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="semver"> | |||
<xs:restriction base="limited-string"> | |||
<xs:pattern | |||
value="(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(\-([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="version"> | |||
<xs:restriction base="limited-string"> | |||
<xs:pattern value="[0-9]+(\.[0-9]+){0,2}"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="url"> | |||
<xs:restriction base="xs:anyURI"> | |||
<xs:pattern value="https?://.+"/> | |||
<xs:maxLength value="256"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="doc-user-url"> | |||
<xs:restriction base="non-empty-string"> | |||
<xs:pattern value="https://.+|user-[a-z]+[\-a-z]*[a-z]+"/> | |||
<xs:maxLength value="256"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="doc-admin-url"> | |||
<xs:restriction base="non-empty-string"> | |||
<xs:pattern value="https://.+|admin-[a-z]+[\-a-z]*[a-z]+"/> | |||
<xs:maxLength value="256"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="doc-developer-url"> | |||
<xs:restriction base="non-empty-string"> | |||
<xs:pattern value="https://.+|developer-[a-z]+[\-a-z]*[a-z]+"/> | |||
<xs:maxLength value="256"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="secure-url"> | |||
<xs:restriction base="xs:anyURI"> | |||
<xs:pattern value="https://.+"/> | |||
<xs:maxLength value="256"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="email"> | |||
<xs:restriction base="limited-string"> | |||
<xs:pattern value="[^@]+@[^\.]+\..+"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<!-- first level elements --> | |||
<xs:complexType name="screenshot"> | |||
<xs:simpleContent> | |||
<xs:extension base="secure-url"> | |||
<xs:attribute name="small-thumbnail" use="optional" | |||
type="secure-url"/> | |||
</xs:extension> | |||
</xs:simpleContent> | |||
</xs:complexType> | |||
<xs:simpleType name="id"> | |||
<xs:restriction base="limited-string"> | |||
<xs:pattern value="[a-z]+[a-z0-9_]*[a-z0-9]+"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:complexType name="author"> | |||
<xs:simpleContent> | |||
<xs:extension base="limited-string"> | |||
<xs:attribute name="mail" type="email" use="optional"/> | |||
<xs:attribute name="homepage" type="url" use="optional"/> | |||
</xs:extension> | |||
</xs:simpleContent> | |||
</xs:complexType> | |||
<xs:complexType name="repository"> | |||
<xs:simpleContent> | |||
<xs:extension base="url"> | |||
<xs:attribute name="type" type="vcs" use="optional" | |||
default="git"/> | |||
</xs:extension> | |||
</xs:simpleContent> | |||
</xs:complexType> | |||
<xs:simpleType name="vcs"> | |||
<xs:restriction base="xs:string"> | |||
<xs:enumeration value="git"/> | |||
<xs:enumeration value="mercurial"/> | |||
<xs:enumeration value="subversion"/> | |||
<xs:enumeration value="bzr"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:complexType name="types"> | |||
<xs:sequence> | |||
<xs:element name="prelogin" minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="filesystem" minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="authentication" minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="logging" minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="dav" minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="prevent_group_restriction" minOccurs="0" | |||
maxOccurs="1"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:simpleType name="category"> | |||
<xs:restriction base="xs:string"> | |||
<xs:enumeration value="security"/> | |||
<xs:enumeration value="customization"/> | |||
<xs:enumeration value="files"/> | |||
<xs:enumeration value="integration"/> | |||
<xs:enumeration value="monitoring"/> | |||
<xs:enumeration value="multimedia"/> | |||
<xs:enumeration value="office"/> | |||
<xs:enumeration value="organization"/> | |||
<xs:enumeration value="social"/> | |||
<xs:enumeration value="tools"/> | |||
<xs:enumeration value="games"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="licence"> | |||
<xs:restriction base="xs:string"> | |||
<xs:enumeration value="agpl"/> | |||
<xs:enumeration value="mpl"/> | |||
<xs:enumeration value="apache"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="databases"> | |||
<xs:restriction base="xs:string"> | |||
<xs:enumeration value="sqlite"/> | |||
<xs:enumeration value="mysql"/> | |||
<xs:enumeration value="pgsql"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:complexType name="documentation"> | |||
<xs:sequence> | |||
<xs:element name="user" type="doc-user-url" minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="admin" type="doc-admin-url" minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="developer" type="doc-developer-url" minOccurs="0" | |||
maxOccurs="1"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="settings"> | |||
<xs:sequence> | |||
<xs:element name="admin" type="php-class" minOccurs="0" | |||
maxOccurs="unbounded"/> | |||
<xs:element name="admin-section" type="php-class" minOccurs="0" | |||
maxOccurs="unbounded"/> | |||
<xs:element name="personal" type="php-class" minOccurs="0" | |||
maxOccurs="unbounded"/> | |||
<xs:element name="personal-section" type="php-class" minOccurs="0" | |||
maxOccurs="unbounded"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="activity"> | |||
<xs:sequence> | |||
<xs:element name="settings" type="activity-settings" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="filters" type="activity-filters" minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="providers" type="activity-providers" minOccurs="0" | |||
maxOccurs="1"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="activity-settings"> | |||
<xs:sequence> | |||
<xs:element name="setting" type="php-class" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="activity-filters"> | |||
<xs:sequence> | |||
<xs:element name="filter" type="php-class" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="activity-providers"> | |||
<xs:sequence> | |||
<xs:element name="provider" type="php-class" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="navigations"> | |||
<xs:sequence> | |||
<xs:element name="navigation" type="navigation" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="navigation"> | |||
<xs:sequence> | |||
<xs:element name="id" type="id" minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="name" type="non-empty-string" minOccurs="1" maxOccurs="1"/> | |||
<xs:element name="route" type="route" minOccurs="1" maxOccurs="1"/> | |||
<xs:element name="icon" type="xs:anyURI" minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="order" type="xs:int" minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="type" type="navigation-type" minOccurs="0" maxOccurs="1"/> | |||
</xs:sequence> | |||
<xs:attribute name="role" type="navigation-role" default="all" use="optional"/> | |||
</xs:complexType> | |||
<xs:simpleType name="navigation-role"> | |||
<xs:restriction base="xs:string"> | |||
<xs:enumeration value="all"/> | |||
<xs:enumeration value="admin"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="navigation-type"> | |||
<xs:restriction base="xs:string"> | |||
<xs:enumeration value="link"/> | |||
<xs:enumeration value="settings"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:simpleType name="route"> | |||
<xs:restriction base="non-empty-string"> | |||
<xs:pattern value="[0-9a-zA-Z_]+(\.[0-9a-zA-Z_]+){2}"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:complexType name="contactsmenu"> | |||
<xs:sequence> | |||
<xs:element name="provider" type="php-class" minOccurs="1" | |||
maxOccurs="1"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="collaboration"> | |||
<xs:sequence> | |||
<xs:element name="plugins" type="collaboration-plugins" minOccurs="0" maxOccurs="1"> | |||
</xs:element> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="collaboration-plugins"> | |||
<xs:sequence> | |||
<xs:element name="plugin" type="collaboration-plugins-plugin" minOccurs="1" maxOccurs="unbounded"> | |||
</xs:element> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="collaboration-plugins-plugin"> | |||
<xs:simpleContent> | |||
<xs:extension base="php-class"> | |||
<xs:attribute name="type" type="collaboration-plugin-type" use="required"/> | |||
<xs:attribute name="share-type" type="share-type" /> | |||
</xs:extension> | |||
</xs:simpleContent> | |||
</xs:complexType> | |||
<xs:simpleType name="collaboration-plugin-type"> | |||
<xs:restriction base="xs:string"> | |||
<xs:enumeration value="collaborator-search"/> | |||
<xs:enumeration value="autocomplete-sort"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:complexType name="sabre"> | |||
<xs:sequence> | |||
<xs:element name="collections" type="sabre-collections" minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="plugins" type="sabre-plugins" minOccurs="0" maxOccurs="1"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="sabre-collections"> | |||
<xs:sequence> | |||
<xs:element name="collection" type="php-class" minOccurs="1" maxOccurs="unbounded"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="sabre-plugins"> | |||
<xs:sequence> | |||
<xs:element name="plugin" type="php-class" minOccurs="1" maxOccurs="unbounded"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:simpleType name="share-type"> | |||
<xs:restriction base="xs:string"> | |||
<xs:enumeration value="SHARE_TYPE_USER"/> | |||
<xs:enumeration value="SHARE_TYPE_GROUP"/> | |||
<xs:enumeration value="SHARE_TYPE_LINK"/> | |||
<xs:enumeration value="SHARE_TYPE_EMAIL"/> | |||
<xs:enumeration value="SHARE_TYPE_CONTACT"/> | |||
<xs:enumeration value="SHARE_TYPE_REMOTE"/> | |||
<xs:enumeration value="SHARE_TYPE_CIRCLE"/> | |||
<xs:enumeration value="SHARE_TYPE_GUEST"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<!-- dependencies --> | |||
<xs:complexType name="dependencies"> | |||
<xs:sequence> | |||
<xs:element name="php" type="php" minOccurs="0" maxOccurs="1"/> | |||
<xs:element name="database" type="database" minOccurs="0" | |||
maxOccurs="unbounded"/> | |||
<xs:element name="command" type="shell-command" minOccurs="0" | |||
maxOccurs="unbounded"/> | |||
<xs:element name="lib" type="min-max-version" minOccurs="0" | |||
maxOccurs="unbounded"/> | |||
<xs:element name="owncloud" type="owncloud" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="nextcloud" type="nextcloud" minOccurs="1" | |||
maxOccurs="1"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="owncloud"> | |||
<xs:attribute name="min-version" type="version" use="required"/> | |||
<xs:attribute name="max-version" type="version" use="optional"/> | |||
</xs:complexType> | |||
<xs:complexType name="nextcloud"> | |||
<xs:attribute name="min-version" type="version" use="required"/> | |||
<xs:attribute name="max-version" type="version" use="required"/> | |||
</xs:complexType> | |||
<xs:simpleType name="shell-command"> | |||
<xs:restriction base="limited-string"> | |||
<xs:pattern value="[a-zA-Z\-_]+"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:complexType name="min-max-version"> | |||
<xs:simpleContent> | |||
<xs:extension base="limited-string"> | |||
<xs:attribute name="min-version" type="version" | |||
use="optional"/> | |||
<xs:attribute name="max-version" type="version" | |||
use="optional"/> | |||
</xs:extension> | |||
</xs:simpleContent> | |||
</xs:complexType> | |||
<xs:complexType name="database"> | |||
<xs:simpleContent> | |||
<xs:extension base="databases"> | |||
<xs:attribute name="min-version" type="version" | |||
use="optional"/> | |||
<xs:attribute name="max-version" type="version" | |||
use="optional"/> | |||
</xs:extension> | |||
</xs:simpleContent> | |||
</xs:complexType> | |||
<xs:complexType name="php"> | |||
<xs:simpleContent> | |||
<xs:extension base="empty-string"> | |||
<xs:attribute name="min-int-size" type="bits" use="optional" | |||
default="32"/> | |||
<xs:attribute name="min-version" type="version" | |||
use="optional"/> | |||
<xs:attribute name="max-version" type="version" | |||
use="optional"/> | |||
</xs:extension> | |||
</xs:simpleContent> | |||
</xs:complexType> | |||
<xs:simpleType name="bits"> | |||
<xs:restriction base="xs:int"> | |||
<xs:enumeration value="32"/> | |||
<xs:enumeration value="64"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
<xs:complexType name="repair-steps"> | |||
<xs:sequence> | |||
<xs:element name="pre-migration" type="steps" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="post-migration" type="steps" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="live-migration" type="steps" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="install" type="steps" minOccurs="0" | |||
maxOccurs="1"/> | |||
<xs:element name="uninstall" type="steps" minOccurs="0" | |||
maxOccurs="1"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="jobs"> | |||
<xs:sequence> | |||
<xs:element name="job" type="php-class" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="steps"> | |||
<xs:sequence> | |||
<xs:element name="step" type="php-class" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="two-factor-providers"> | |||
<xs:sequence> | |||
<xs:element name="provider" type="php-class" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:complexType name="commands"> | |||
<xs:sequence> | |||
<xs:element name="command" type="php-class" minOccurs="1" | |||
maxOccurs="unbounded"/> | |||
</xs:sequence> | |||
</xs:complexType> | |||
<xs:simpleType name="php-class"> | |||
<xs:restriction base="xs:string"> | |||
<xs:pattern | |||
value="[a-zA-Z_][0-9a-zA-Z_]*(\\[a-zA-Z_][0-9a-zA-Z_]*)*"/> | |||
</xs:restriction> | |||
</xs:simpleType> | |||
</xs:schema> |
@@ -1,9 +0,0 @@ | |||
<?xml version="1.0"?> | |||
<info> | |||
<id>testapp-infoxml-version</id> | |||
<version>1.2.3</version> | |||
<author>Jane</author> | |||
<description>A b c</description> | |||
<licence>Abc</licence> | |||
<name>Test app</name> | |||
</info> |
@@ -1,12 +0,0 @@ | |||
<?xml version="1.0"?> | |||
<info> | |||
<id>testapp-infoxml</id> | |||
<version>1.2.3</version> | |||
<author>Jane</author> | |||
<description>A b c</description> | |||
<licence>Abc</licence> | |||
<name>Test app</name> | |||
<dependencies> | |||
<nextcloud min-version="12.0" max-version="12.0"/> | |||
</dependencies> | |||
</info> |
@@ -1,11 +0,0 @@ | |||
<?xml version="1.0"?> | |||
<info> | |||
<id>testapp-version</id> | |||
<version>1.1.1</version> | |||
<author>Jane</author> | |||
<description>A b c</description> | |||
<licence>Abc</licence> | |||
<dependencies> | |||
<nextcloud min-version="12.0" max-version="12.0"/> | |||
</dependencies> | |||
</info> |
@@ -1,11 +0,0 @@ | |||
<?xml version="1.0"?> | |||
<info> | |||
<id>testapp-version</id> | |||
<author>Jane</author> | |||
<description>A b c</description> | |||
<licence>Abc</licence> | |||
<name>Test app</name> | |||
<dependencies> | |||
<nextcloud min-version="12.0" max-version="12.0"/> | |||
</dependencies> | |||
</info> |
@@ -1 +0,0 @@ | |||
1.2.3 |
@@ -0,0 +1,13 @@ | |||
<?xml version="1.0"?> | |||
<info xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd"> | |||
<id>testapp_infoxml</id> | |||
<name>Test app</name> | |||
<summary>A b c</summary> | |||
<description>A b c</description> | |||
<version>1.2.3</version> | |||
<licence>agpl</licence> | |||
<author>Jane</author> | |||
<category>games</category> | |||
<bugs>https://example.org</bugs> | |||
</info> |
@@ -0,0 +1,16 @@ | |||
<?xml version="1.0"?> | |||
<info xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd"> | |||
<id>testapp_infoxml</id> | |||
<name>Test app</name> | |||
<summary>A b c</summary> | |||
<description>A b c</description> | |||
<version>1.2.3</version> | |||
<licence>agpl</licence> | |||
<author>Jane</author> | |||
<category>games</category> | |||
<bugs>https://example.org</bugs> | |||
<dependencies> | |||
<nextcloud min-version="12.0" max-version="12.0"/> | |||
</dependencies> | |||
</info> |
@@ -0,0 +1,15 @@ | |||
<?xml version="1.0"?> | |||
<info xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd"> | |||
<id>testapp_name_missing</id> | |||
<summary>A b c</summary> | |||
<description>A b c</description> | |||
<version>1.2.3</version> | |||
<licence>agpl</licence> | |||
<author>Jane</author> | |||
<category>games</category> | |||
<bugs>https://example.org</bugs> | |||
<dependencies> | |||
<nextcloud min-version="12.0" max-version="12.0"/> | |||
</dependencies> | |||
</info> |
@@ -0,0 +1,15 @@ | |||
<?xml version="1.0"?> | |||
<info xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd"> | |||
<id>testapp_version</id> | |||
<name>Test app</name> | |||
<summary>A b c</summary> | |||
<description>A b c</description> | |||
<licence>agpl</licence> | |||
<author>Jane</author> | |||
<category>games</category> | |||
<bugs>https://example.org</bugs> | |||
<dependencies> | |||
<nextcloud min-version="12.0" max-version="12.0"/> | |||
</dependencies> | |||
</info> |
@@ -44,19 +44,21 @@ class InfoCheckerTest extends TestCase { | |||
protected function setUp() { | |||
parent::setUp(); | |||
$this->infoChecker = new InfoChecker(new InfoParser()); | |||
$this->infoChecker = new InfoChecker(\OC::$server->getAppManager()); | |||
} | |||
public function appInfoData() { | |||
return [ | |||
['testapp-infoxml', []], | |||
['testapp-version', [['type' => 'mandatoryFieldMissing', 'field' => 'version']]], | |||
['testapp-dependency-missing', [ | |||
['type' => 'missingRequirement', 'field' => 'min'], | |||
['type' => 'missingRequirement', 'field' => 'max'], | |||
['type' => 'mandatoryFieldMissing', 'field' => 'dependencies'], | |||
['testapp_infoxml', []], | |||
['testapp_version', [ | |||
['type' => 'parseError', 'field' => 'Element \'licence\': This element is not expected. Expected is one of ( description, version ).' . "\n"], | |||
]], | |||
['testapp_dependency_missing', [ | |||
['type' => 'parseError', 'field' => 'Element \'info\': Missing child element(s). Expected is one of ( repository, screenshot, dependencies ).' . "\n"], | |||
]], | |||
['testapp_name_missing', [ | |||
['type' => 'parseError', 'field' => 'Element \'summary\': This element is not expected. Expected is ( name ).' . "\n"], | |||
]], | |||
['testapp-name-missing', [['type' => 'mandatoryFieldMissing', 'field' => 'name']]], | |||
]; | |||
} | |||
@@ -68,6 +70,7 @@ class InfoCheckerTest extends TestCase { | |||
*/ | |||
public function testApps($appId, $expectedErrors) { | |||
$errors = $this->infoChecker->analyse($appId); | |||
libxml_clear_errors(); | |||
$this->assertEquals($expectedErrors, $errors); | |||
} |