Page Menu
Home
DevCentral
Search
Configure Global Search
Log In
Files
F3773684
D1442.id3708.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
25 KB
Referenced Files
None
Subscribers
None
D1442.id3708.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D1442: Provide configuration for wikis
Attached
Detach File
Event Timeline
Log In to Comment