Page MenuHomeDevCentral

D1442.id3708.diff
No OneTemporary

D1442.id3708.diff

Index: .env.example
===================================================================
--- /dev/null
+++ .env.example
@@ -0,0 +1,5 @@
+MEDIAWIKI_ENTRY_POINT="/srv/mediawiki/index.php"
+MEDIAWIKI_SECRET_KEY="generate a 64 characters hexadecimal key"
+DB_HOST="localhost"
+DB_USER="root"
+DB_PASS=""
Index: .gitignore
===================================================================
--- /dev/null
+++ .gitignore
@@ -0,0 +1,6 @@
+# Composer
+/vendor/
+composer.lock
+
+# DotEnv
+.env
Index: LocalSettings.php
===================================================================
--- /dev/null
+++ LocalSettings.php
@@ -0,0 +1,44 @@
+<?php
+
+use Nasqueron\SAAS\MediaWiki\Configuration\CommonSettings;
+use Nasqueron\SAAS\MediaWiki\Service;
+use Nasqueron\SAAS\MediaWiki\Environment;
+
+require_once __DIR__ . '/vendor/autoload.php';
+
+Environment::load();
+$service = Service::preload();
+$service->run();
+$serviceConfiguration = $service->getConfiguration();
+
+$localDatabases = $serviceConfiguration->getLocalDatabases();
+
+$wgConf->wikis = $localDatabases;
+$wgConf->localVHosts = [ 'localhost' ];
+$wgConf->settings = $serviceConfiguration->getSettings();
+$wgConf->suffixes = $localDatabases;
+$wgConf->siteParamsCallback = 'Nasqueron\SAAS\MediaWiki\Hooks::onSiteParameters';
+
+$wgDBname = $serviceConfiguration->getSelectedDatabase();
+
+$wgDBserver = $_ENV['DB_HOST'];
+$wgDBuser = $_ENV['DB_USER'];
+$wgDBpassword = $_ENV['DB_PASS'];
+
+if (Environment::isBSD()) {
+ CommonSettings::fixExecutablePaths($wgConf->settings, "/usr/local/bin");
+}
+
+$wgConf->extractAllGlobals( $wgDBname );
+CommonSettings::fixGroupPermissions($saasExtraGroupPermissions);
+
+$wgScriptPath = Environment::get("MEDIAWIKI_DIRECTORY", "");
+$wgScript = "{$wgScriptPath}/index.php";
+$wgSecretKey = $_ENV["MEDIAWIKI_SECRET_KEY"];
+
+$wgCacheDirectory = $serviceConfiguration->getCacheDir();
+$wgFileCacheDirectory = $wgCacheDirectory . "/pages";
+$wgUploadDirectory = $serviceConfiguration->getDataStoreDir() . "/images";
+
+wfLoadExtensions($serviceConfiguration->getResources('Extension'));
+wfLoadSkins($serviceConfiguration->getResources('Skin'));
Index: composer.json
===================================================================
--- /dev/null
+++ composer.json
@@ -0,0 +1,29 @@
+{
+ "name": "nasqueron/saas-mediawiki",
+ "description": "SaaS configuration entry point for MediaWiki",
+ "keywords": [
+ "nasqueron",
+ "SAAS",
+ "mediawiki",
+ "farm"
+ ],
+ "type": "project",
+ "license": "BSD-2-Clause",
+ "authors": [
+ {
+ "name": "Sébastien Santoro",
+ "email": "dereckson@espace-win.org"
+ }
+ ],
+ "require": {
+ "vlucas/phpdotenv": "dev-master",
+ "nasqueron/saas-service": "^0.0.1"
+ },
+ "autoload": {
+ "psr-4": {
+ "Nasqueron\\SAAS\\MediaWiki\\": "src/",
+ "Nasqueron\\SAAS\\MediaWiki\\Configuration\\": "config/",
+ "Nasqueron\\SAAS\\MediaWiki\\Tests\\": "tests/"
+ }
+ }
+}
Index: config/CommonSettings.php
===================================================================
--- /dev/null
+++ config/CommonSettings.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace Nasqueron\SAAS\MediaWiki\Configuration;
+
+use Nasqueron\SAAS\ConfigurationException;
+use Nasqueron\SAAS\MediaWiki\WithExecutablesPathsFix;
+
+class CommonSettings {
+
+ use WithExecutablesPathsFix;
+
+ ///
+ /// Individual set of settings
+ ///
+
+ /**
+ * @throws ConfigurationException
+ */
+ private static function getRightsSettings (array $licenses) : array {
+ $settings = [];
+ foreach ($licenses as $key => $license) {
+ switch ($license) {
+ case 'CC-BY 4.0':
+ $settings['wgRightsUrl'][$key] = 'http://creativecommons.org/licenses/by/4.0/';
+ $settings['wgRightsText'][$key] = 'Creative Commons Attribution 4.0 International License';
+ $settings['wgRightsIcon'][$key] = 'https://i.creativecommons.org/l/by/4.0/88x31.png';
+ break;
+
+ default:
+ throw new ConfigurationException("License unknown: $license");
+ }
+ }
+ return $settings;
+ }
+
+ /**
+ * Replace default permissions by custom permissions.
+ */
+ public static function fixGroupPermissions ($groupPermissionsToOverride) {
+ // Code from WMF wmf-config/CommonSettings.php (groupOverrides)
+
+ // PHP array merge keep value already defined, but here we want to
+ // override those values by the new ones.
+
+ global $wgGroupPermissions;
+
+ foreach ($groupPermissionsToOverride as $group => $permissions) {
+ if (!array_key_exists( $group, $wgGroupPermissions)) {
+ $wgGroupPermissions[$group] = [];
+ }
+
+ $wgGroupPermissions[$group] = $permissions
+ + $wgGroupPermissions[$group];
+ }
+ }
+
+ ///
+ /// Helper methods to apply those settings fix
+ ///
+
+ /**
+ * @throws ConfigurationException
+ */
+ public static function mapSettings (array &$settings) : void {
+ $settings += self::getMappedSettings($settings);
+ }
+
+ /**
+ * @throws ConfigurationException
+ */
+ public static function getMappedSettings (array $settings) : array {
+ $mappedSettings = [];
+ $mappedSettings += self::getRightsSettings($settings['saasLicense']);
+ return $mappedSettings;
+ }
+
+}
Index: config/Instances.php
===================================================================
--- /dev/null
+++ config/Instances.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Nasqueron\SAAS\MediaWiki\Configuration;
+
+use Nasqueron\SAAS\MediaWiki\InstancesRepository;
+
+class Instances extends InstancesRepository {
+
+ static public function getList () : array {
+ return [
+ // Format: => database name
+
+ "agora.nasqueron.org" => "nasqueron_wiki",
+ "arsmagica.espace-win.org" => "arsmagica",
+ "utopia.espace-win.org" => "utopia",
+ "www.wolfplex.org" => "wolfplexdb",
+ ];
+ }
+
+ static public function getAliases () : array {
+ return [
+ // Format: Database => [ hosts ]
+
+ "wolfplexdb" => [
+ "www.wolfplex.be",
+ "wiki.wolfplex.org",
+ "wiki.wolfplex.be",
+ ]
+ ];
+ }
+
+}
Index: config/MappableSettings.php
===================================================================
--- /dev/null
+++ config/MappableSettings.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace Nasqueron\SAAS\MediaWiki\Configuration;
+
+///
+/// Temporary hack to get clean configuration.
+///
+/// Plan is to deploy the MediaWiki SaaS on a dedicated node.
+///
+/// Meanwhile, as we share the main Nasqueron MySQL database,
+/// with an history of databases going back to 2001, we need
+/// to map nicely named site key to actual databases names.
+
+abstract class MappableSettings {
+
+ abstract static public function getDatabaseMap() : array;
+ abstract static public function getMappedSettings() : array;
+
+ static public function getSettings () : array {
+ $settings = [];
+ foreach (static::getMappedSettings() as $setting => $values) {
+ $settings[$setting] = self::mapDatabases($values);
+ }
+ return $settings;
+ }
+
+ static private function mapDatabases ($items) {
+ $setting = [];
+ foreach ($items as $key => $value) {
+ $mappedKey = self::mapDatabase($key);
+ $setting[$mappedKey] = $value;
+ }
+ return $setting;
+ }
+
+ static private function mapDatabase ($key) {
+ foreach (static::getDatabaseMap() as $canonical => $actual) {
+ if ($key === $canonical) {
+ return $actual;
+ }
+ }
+
+ return $key;
+ }
+
+}
Index: config/Settings.php
===================================================================
--- /dev/null
+++ config/Settings.php
@@ -0,0 +1,130 @@
+<?php
+
+namespace Nasqueron\SAAS\MediaWiki\Configuration;
+
+class Settings extends MappableSettings {
+
+ static public function getDatabaseMap () : array {
+ return [
+ 'agora' => 'nasqueron_wiki',
+ 'wolfplex' => 'wolfplexdb',
+ ];
+ }
+
+ static public function getMappedSettings () : array {
+ return [
+
+ ///
+ /// MediaWiki Core
+ ///
+
+ 'wgArticlePath' => [
+ 'default' => '/wiki/$1',
+ 'agora' => '/$1',
+ 'arsmagica' => '/$1',
+ ],
+
+ 'wgDBprefix' => [
+ 'default' => '',
+
+ // Legacy installations
+ 'arsmagica' => 'arsm_',
+ 'utopia' => 'wiki_',
+ 'wolfplex' => 'mw_', // shared database
+ ],
+
+ 'wgDefaultSkin' => [
+ 'default' => 'vector',
+ 'agora' => 'timeless',
+ // TODO: find utopia skin
+ ],
+
+ 'wgEnableCreativeCommonsRdf' => [
+ 'default' => true,
+ ],
+
+ 'wgEnableDublinCoreRdf' => [
+ 'default' => true,
+ ],
+
+ 'wgEnableUploads' => [
+ 'default' => false,
+ ],
+
+ 'wgInstantCommons' => [
+ 'default' => true,
+ ],
+
+ 'wgLanguageCode' => [
+ 'default' => 'en',
+ 'arsmagica' => 'fr',
+ 'utopia' => 'fr',
+ ],
+
+ 'wgLogo' => [
+ 'default' => '/images/b/bc/Wiki.png',
+ 'agora' => 'https://assets.nasqueron.org/logos/logo-main-133px.png'
+ ],
+
+ 'wgNamespacesWithSubpages' => [
+ 'wolfplex' => [
+ NS_MAIN => true,
+ ],
+ 'agora' => [
+ NS_MAIN => true,
+ ],
+ ],
+
+ 'wgSitename' => [
+ 'default' => 'Wiki',
+ 'agora' => 'Nasqueron Agora',
+ 'arsmagica' => 'Ars Magica',
+ 'utopia' => 'Utopia',
+ 'wolfplex' => 'Wolfplex',
+ ],
+
+ 'wgUseFileCache' => [
+ 'default' => false,
+ ],
+
+ 'wgUseGzip' => [
+ 'default' => true,
+ ],
+
+ 'saasExtraGroupPermissions' => [
+ 'default' => [
+ '*' => [
+ 'edit' => false,
+ 'createaccount' => false,
+ 'foo' => true,
+ ],
+ ]
+ ],
+
+ 'saasLicense' => [
+ 'default' => 'CC-BY 4.0',
+ ],
+
+ ///
+ /// Skins and extensions
+ ///
+
+ 'saasUseExtensionCite' => [
+ 'default' => true,
+ ],
+
+ 'saasUseSkinMonoBook' => [
+ 'default' => true,
+ ],
+
+ 'saasUseSkinVector' => [
+ 'default' => true,
+ ],
+
+ 'saasUseSkinTimeless' => [
+ 'default' => true,
+ ],
+ ];
+ }
+
+}
Index: index.php
===================================================================
--- /dev/null
+++ index.php
@@ -0,0 +1,21 @@
+<?php
+
+use Nasqueron\SAAS\MediaWiki\Service;
+use Nasqueron\SAAS\MediaWiki\Environment;
+
+/**
+ * The Composer autoloader is used to load required libraries.
+ * This service follows PSR-4 conventions.
+ *
+ * The environment is read from environment variables or an .env file.
+ *
+ * This entry point check the host and call MediaWiki if it exists.
+ * If not, it serves a 404.
+ */
+
+require_once __DIR__ . '/vendor/autoload.php';
+
+Environment::load();
+$service = Service::preload();
+
+require $service->getEntryPoint();
Index: src/Configuration.php
===================================================================
--- /dev/null
+++ src/Configuration.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace Nasqueron\SAAS\MediaWiki;
+
+use Nasqueron\SAAS\MediaWiki\Configuration\CommonSettings;
+use Nasqueron\SAAS\MediaWiki\Configuration\Instances;
+use Nasqueron\SAAS\MediaWiki\Configuration\Settings;
+
+class Configuration {
+
+ /**
+ * @var string
+ */
+ private $host;
+
+ public function __construct (string $host) {
+ $this->host = $host;
+ }
+
+ public function getLocalDatabases () : array {
+ return array_values(Instances::getList());
+ }
+
+ public function getSettings () : array {
+ // wg… keys
+ $settings = Settings::getSettings();
+
+ // saas… → wg… keys
+ CommonSettings::mapSettings($settings);
+
+ return $settings;
+ }
+
+ public function getResources (string $type) : array {
+ // saasUse<type><resource name>
+ // e.g. saasUseExtensionCite or saasUseSkinTimeless
+
+ $resources = [];
+
+ $prefix = "saasUse" . $type;
+ $len = strlen($prefix);
+ foreach ($GLOBALS as $key => $value) {
+ if (substr($key, 0, $len) === $prefix) {
+ $resources[] = substr($key, $len);
+ }
+ }
+
+ return $resources;
+ }
+
+ public function getCacheDir () {
+ $cacheRootDir = Environment::get("CACHE_DIRECTORY", "/var/cache/mediawiki");
+ return $cacheRootDir . '/' . $this->host;
+ }
+
+ public function getDataStoreDir () {
+ $dataStoreDir = Environment::get("DATASTORE_DIRECTORY", "/var/dataroot");
+ return $dataStoreDir . '/' . $this->host;
+ }
+
+ /**
+ * @throws \Nasqueron\SAAS\InstanceNotFoundException
+ */
+ public function getSelectedDatabase () {
+ return Instances::getDatabaseFromHost($this->host);
+ }
+
+ ///
+ /// Helper methods
+ ///
+
+ public static function isSelectedWiki (string $wiki, string $suffix) : bool {
+ return substr($wiki, -strlen($suffix)) == $suffix;
+ }
+
+}
Index: src/Environment.php
===================================================================
--- /dev/null
+++ src/Environment.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Nasqueron\SAAS\MediaWiki;
+
+use Dotenv\Dotenv;
+use Dotenv\Exception\ValidationException;
+
+class Environment {
+
+ /**
+ * @var bool
+ */
+ static private $isLoaded = false;
+
+ /**
+ * Loads the environment, if it hasn't been loaded before.
+ */
+ public static function load () : void {
+ if (!self::$isLoaded) {
+ $dotenv = new Dotenv(self::getDirectory());
+ $dotenv->safeLoad();
+ try {
+ $dotenv->required(self::getRequiredVariables());
+ } catch (ValidationException $exception) {
+ Service::serveInternalErrorResponse($exception);
+ }
+ self::$isLoaded = true;
+ }
+ }
+
+ public static function get ($variableName, $defaultValue = "") : string {
+ return $_ENV[$variableName] ?? $defaultValue;
+ }
+
+ private static function getDirectory () : string {
+ return dirname(__DIR__);
+ }
+
+ private static function getRequiredVariables () : array {
+ return [
+ 'MEDIAWIKI_ENTRY_POINT',
+ 'MEDIAWIKI_SECRET_KEY',
+ 'DB_HOST',
+ 'DB_USER',
+ 'DB_PASS',
+ ];
+ }
+
+ public static function isBSD () : bool {
+ $system = php_uname("s");
+ return $system == "FreeBSD" || $system == "OpenBSD" || $system == "NetBSD";
+ }
+}
Index: src/Hooks.php
===================================================================
--- /dev/null
+++ src/Hooks.php
@@ -0,0 +1,31 @@
+<?php
+
+namespace Nasqueron\SAAS\MediaWiki;
+
+class Hooks {
+
+ public static function onSiteParameters ($conf, $wiki) {
+ $site = null;
+ $lang = null;
+
+ foreach ($conf->suffixes as $suffix) {
+ if (Configuration::isSelectedWiki($wiki, $suffix)) {
+ $site = $suffix;
+ $lang = substr( $wiki, 0, -strlen( $suffix ) );
+ break;
+ }
+ }
+
+ return [
+ 'suffix' => $site,
+ 'lang' => $lang,
+ 'params' => [
+ 'lang' => $lang,
+ 'site' => $site,
+ 'wiki' => $wiki,
+ ],
+ 'tags' => [],
+ ];
+ }
+
+}
Index: src/InstancesRepository.php
===================================================================
--- /dev/null
+++ src/InstancesRepository.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace Nasqueron\SAAS\MediaWiki;
+
+use Nasqueron\SAAS\InstanceNotFoundException;
+
+abstract class InstancesRepository {
+
+ ///
+ /// Repository data methods
+ ///
+
+ abstract static public function getList () : array;
+ abstract static public function getAliases () : array;
+
+ ///
+ /// Helper methods
+ ///
+
+ /**
+ * @throws InstanceNotFoundException
+ */
+ public static function getCanonicalHost (string $host) : string {
+ $canonicalList = static::getList();
+
+ // Case 1 - the host is canonical
+ if (array_key_exists($host, $canonicalList)) {
+ return $host;
+ }
+
+ // Case 2 - the host is an alias
+ foreach (static::getAliases() as $database => $vhosts) {
+ if (in_array($host, $vhosts)) {
+ return array_search($database, $canonicalList);
+ }
+ }
+
+ throw new InstanceNotFoundException($host);
+ }
+
+ /**
+ * @throws InstanceNotFoundException
+ */
+ public static function getDatabaseFromHost (string $host) : string {
+ $canonicalHost = self::getCanonicalHost($host);
+ return static::getList()[$canonicalHost];
+ }
+
+}
Index: src/Service.php
===================================================================
--- /dev/null
+++ src/Service.php
@@ -0,0 +1,123 @@
+<?php
+
+namespace Nasqueron\SAAS\MediaWiki;
+
+use Nasqueron\SAAS\InstanceNotFoundException;
+use Nasqueron\SAAS\SaaSException;
+use Nasqueron\SAAS\Service as BaseService;
+use Nasqueron\SAAS\MediaWiki\Configuration\Instances;
+
+use Exception;
+
+class Service extends BaseService {
+
+ /**
+ * @var string
+ */
+ private $host;
+
+ /**
+ * @var string
+ */
+ private $canonicalHost = "";
+
+ /**
+ * @var Configuration
+ */
+ private $configuration = null;
+
+ /**
+ * @var Service
+ */
+ private static $service = null;
+
+ public function __construct (string $host = '') {
+ $this->host = $host ?: self::getServerHost();
+ }
+
+ /**
+ * @return Service
+ */
+ public static function preload()
+ {
+ if (self::$service === null) {
+ self::$service = new self;
+ try {
+ self::$service
+ ->handleAliveRequest()
+ ->handleNotExistingSite();
+ } catch (SaaSException $exception) {
+ self::$service->serveNotAvailableResponse();
+ }
+ }
+
+ return self::$service;
+
+ }
+
+ public function getHost () : string {
+ return $this->host;
+ }
+
+ public function run () : void {
+ $this->decorateHeaders();
+ }
+
+ public function isExisting () : bool {
+ try {
+ Instances::getDatabaseFromHost($this->host);
+ } catch (InstanceNotFoundException $exception) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private function decorateHeaders () : void {
+ header("SaaS-Host: " . $this->host);
+ header("SaaS-Canonical-Host: " . $this->getCanonicalHost());
+ header("SaaS-App: MediaWiki");
+ }
+
+ public function serveNotExistingResponse() : void {
+ header("HTTP/1.0 404 Not Found");
+
+ require __DIR__ . '/../views/404.php';
+ die;
+ }
+
+ public function serveNotAvailableResponse() : void {
+ header("HTTP/1.0 503 Service Unavailable");
+
+ require __DIR__ . '/../views/503.php';
+ die;
+ }
+
+ public static function serveInternalErrorResponse(Exception $exception) : void {
+ header("HTTP/1.0 500 Internal Error");
+
+ require __DIR__ . '/../views/500.php';
+ die;
+ }
+
+ public function getEntryPoint() : string {
+ return $_ENV['MEDIAWIKI_ENTRY_POINT'];
+ }
+
+ public function getConfiguration() : Configuration {
+ if ($this->configuration === null) {
+ $this->configuration = new Configuration($this->getCanonicalHost());
+ }
+
+ return $this->configuration;
+ }
+
+ public function getCanonicalHost () : string {
+ if ($this->canonicalHost === "") {
+ $this->canonicalHost = Instances::getCanonicalHost($this->getHost());
+ }
+
+ return $this->canonicalHost;
+ }
+
+}
Index: src/WithExecutablesPathsFix.php
===================================================================
--- /dev/null
+++ src/WithExecutablesPathsFix.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Nasqueron\SAAS\MediaWiki;
+
+trait WithExecutablesPathsFix {
+
+ /**
+ * Add settings to set binary paths for executables.
+ *
+ * By default MediaWiki hardcodes Linux-centric paths for binaries.
+ * Other OSes could store elsewhere binaries, like in /usr/local/bin
+ *
+ * @param array $settings
+ * @param string $path
+ */
+ public static function fixExecutablePaths (array &$settings, string $path) {
+ foreach (self::getExecutables() as $setting => $executable) {
+ $settings[$setting]['default'] = $path . '/' . $executable;
+ }
+ }
+
+ private static function getExecutables (): array {
+ return [
+ "wgExiftool" => "exiftool",
+ "wgExiv2Command" => "exiv2",
+ "wgGitBin" => "git",
+ "wgImageMagickConvertCommand" => "convert",
+ "wgJpegTran" => "jpegtran",
+ "wgPhpCli" => "php",
+ ];
+ }
+}
Index: views/404.php
===================================================================
--- /dev/null
+++ views/404.php
@@ -0,0 +1,25 @@
+<!doctype html>
+<html class="no-js" lang="en">
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="x-ua-compatible" content="ie=edge">
+ <title>SaaS :: Instance not found</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+</head>
+<body>
+
+<h1>Service not found</h1>
+<p>The <?= $this->getHost() ?> instance of your service is not available.</p>
+<p>This generally means the domain name has been configured to point to
+ this server, but is not declared in the configuration.</p>
+<h2>Is this a new wiki?</h2>
+<p>This entry point serves several wikis. If you're creating a new wiki, this message means
+ you've successfully configured the DNS and the front-end web server.</p>
+<h3>What should I do now?</h3>
+<p>The next step is to declare your wiki to the <strong>saas-mediawiki</strong> repository.</p>
+<p>Create the wiki database if still not done and
+ declare the host / database pair to the <kbd>config/Instances.php</kbd> file.</p>
+<p>This repository is also where you can customize the wiki settings,
+in the <kbd>config/Settings.php</kbd> file.<br />You probably want to customize site name there.</p>
+<p>You've done all those steps? Reload php-fpm to reset caching or troubleshoot those files.</p>
+</html>
Index: views/500.php
===================================================================
--- /dev/null
+++ views/500.php
@@ -0,0 +1,24 @@
+<!doctype html>
+<html class="no-js" lang="en">
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="x-ua-compatible" content="ie=edge">
+ <title>SaaS :: Server internal error</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+</head>
+<body>
+
+<h1>Service entry point error</h1>
+<h2>Service configuration issue</h2>
+<p>The following exception occurred at the SaaS MediaWiki entry point:</p>
+<p style="font-weight: bold; color: darkred;"><?= $exception->getMessage() ?></p>
+<h2>What I can do?</h2>
+<h3>You're a visitor?</h3>
+<p>This issue could be temporary and means the service is restarting. Try again in a few moment.</p>
+<p>If this error persists, you can report this issue on IRC FreeNode #nasqueron-ops or our
+ <a href="https://devcentral.nasqueron.org/maniphest/task/edit/form/4/">issue tracker</a><br />
+ Include the text of the exception above.
+</p>
+<h3>You're maintainer?</h3>
+<p>This application code is not a part of Mediawiki, but of the SaaS service calling it.<br />It can be found in the <a href="https://devcentral.nasqueron.org/source/saas-mediawiki/">saas-mediawiki</a> repository.</p>
+</html>
Index: views/503.php
===================================================================
--- /dev/null
+++ views/503.php
@@ -0,0 +1,14 @@
+<!doctype html>
+<html class="no-js" lang="en">
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="x-ua-compatible" content="ie=edge">
+ <title>SaaS :: Instance not available</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+</head>
+<body>
+
+<p>The service is not available. Please retry in a few moment.</p>
+
+</body>
+</html>

File Metadata

Mime Type
text/plain
Expires
Mon, Nov 25, 08:07 (21 h, 40 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2262205
Default Alt Text
D1442.id3708.diff (25 KB)

Event Timeline