Page MenuHomeDevCentral

D1442.id3685.diff
No OneTemporary

D1442.id3685.diff

Index: .env.example
===================================================================
--- /dev/null
+++ .env.example
@@ -0,0 +1,4 @@
+MEDIAWIKI_ENTRY_POINT="/srv/mediawiki/index.php"
+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,22 @@
+<?php
+
+$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'];
+
+$wgConf->extractAllGlobals( $wgDBname );
+
+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": "^2.4"
+ },
+ "autoload": {
+ "psr-4": {
+ "Nasqueron\\SAAS\\": "lib-to-move-to-saas-service/",
+ "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,48 @@
+<?php
+
+namespace Nasqueron\SAAS\MediaWiki\Configuration;
+
+use Nasqueron\SAAS\ConfigurationException;
+
+class CommonSettings {
+
+ /**
+ * @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;
+ }
+
+ ///
+ /// 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;
+ }
+}
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.be" => "wolfplexdb",
+ ];
+ }
+
+ static public function getAliases () : array {
+ return [
+ // Format: Database => [ hosts ]
+
+ "wolfplexdb" => [
+ "www.wolfplex.org",
+ "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,98 @@
+<?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 [
+ 'wgDBprefix' => [
+ 'default' => '',
+
+ // Legacy installations
+ 'arsmagica' => 'arsm_',
+ 'utopia' => 'wiki_',
+ 'wolfplex' => 'mw_', // shared database
+ ],
+
+ 'wgSitename' => [
+ 'agora' => 'Nasqueron Agora',
+ 'arsmagica' => 'Ars Magica',
+ 'utopia' => 'Utopia',
+ 'wolfplex' => 'Wolfplex',
+ ],
+
+ 'wgLanguageCode' => [
+ 'default' => 'en',
+ 'arsmagica' => 'fr',
+ 'utopia' => 'fr',
+ ],
+
+ 'wgArticlePath' => [
+ 'default' => '/wiki/$1',
+ 'arsmagica' => '/$1',
+ ],
+
+ 'wgUploadDirectory' => [
+ 'agora' => '',
+ 'arsmagica' => '',
+ 'utopia' => '',
+ 'wolfplex' => '',
+ ],
+
+ 'wgLogo' => [
+ // Do we serve /w/images for each wiki?
+ 'default' => '/w/images/b/bc/Wiki.png',
+ ],
+
+ 'wgEnableUploads' => [
+ 'default' => false,
+ ],
+
+ 'wgNamespacesWithSubpages' => [
+ 'wolfplex' => [
+ NS_MAIN => true,
+ ],
+ 'nasqueron' => [
+ NS_MAIN => true,
+ ],
+ ],
+
+ 'saasLicense' => [
+ 'default' => 'CC-BY 4.0',
+ ],
+
+ 'wgEnableCreativeCommonsRdf' => [
+ 'default' => true,
+ ],
+
+ 'wgEnableDublinCoreRdf' => [
+ 'default' => true,
+ ],
+
+ 'saasUseExtensionCite' => [
+ 'default' => true,
+ ],
+
+ 'saasUseSkinMonoBook' => [
+ 'default' => true,
+ ],
+
+ 'saasUseSkinVector' => [
+ 'default' => true,
+ ],
+
+ 'saasUseSkinTimeless' => [
+ 'default' => true,
+ ],
+ ];
+ }
+
+}
Index: index.php
===================================================================
--- /dev/null
+++ index.php
@@ -0,0 +1,11 @@
+<?php
+
+use Nasqueron\SAAS\MediaWiki\Service;
+
+// Composer PSR-4 autoload. Allow to use an .env file or the environment.
+require __DIR__ . '/vendor/autoload.php';
+(new Dotenv\Dotenv(__DIR__))->load();
+
+$service = Service::preload();
+
+require $service->getEntryPoint();
Index: lib-to-move-to-saas-service/ConfigurationException.php
===================================================================
--- /dev/null
+++ lib-to-move-to-saas-service/ConfigurationException.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace Nasqueron\SAAS;
+
+
+class ConfigurationException extends SaaSException {
+
+}
Index: lib-to-move-to-saas-service/InstanceNotFoundException.php
===================================================================
--- /dev/null
+++ lib-to-move-to-saas-service/InstanceNotFoundException.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Nasqueron\SAAS;
+
+use Throwable;
+
+class InstanceNotFoundException extends SaaSException {
+
+ /**
+ * @var string
+ */
+ private $instance;
+
+ /**
+ * @var string
+ */
+ const DEFAULT_MESSAGE = "The specified instance can't been found.";
+
+ public function __construct (string $instance, string $message = "",
+ int $code = 0, Throwable $previous = null) {
+ $this->instance = $instance;
+
+ if ($message === "") {
+ $message = self::DEFAULT_MESSAGE;
+ }
+
+ parent::__construct($message, $code, $previous);
+ }
+
+ public function getInstance () : string {
+ return $this->instance;
+ }
+
+ public function setInstance (string $instance) : void {
+ $this->instance = $instance;
+ }
+
+}
\ No newline at end of file
Index: lib-to-move-to-saas-service/SaaSException.php
===================================================================
--- /dev/null
+++ lib-to-move-to-saas-service/SaaSException.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace Nasqueron\SAAS;
+
+
+class SaaSException extends \Exception {
+
+}
Index: lib-to-move-to-saas-service/Service.php
===================================================================
--- /dev/null
+++ lib-to-move-to-saas-service/Service.php
@@ -0,0 +1,100 @@
+<?php
+
+namespace Nasqueron\SAAS;
+
+abstract class Service {
+
+ ///
+ /// Request methods
+ ///
+
+ public function handleNotExistingSite() : Service {
+ if (!$this->isExisting()) {
+ $this->serveNotExistingResponse();
+ }
+
+ return $this;
+ }
+
+ /**
+ * @throws SaasException where the URI can't be found
+ */
+ public function handleAliveRequest() : Service {
+ // Handles /status requests
+ if ($this->isAliveRequest()) {
+ $this->serveAliveResponse();
+ }
+
+ return $this;
+ }
+
+ public abstract function run();
+
+
+ ///
+ /// Default implementation
+ ///
+
+ public function isExisting () : bool {
+ return true;
+ }
+
+ public function serveAliveResponse() : void {
+ die("ALIVE");
+ }
+
+ public function serveNotExistingResponse(): void {
+ header("HTTP/1.0 404 Not Found");
+ die("This site doesn't exist.");
+ }
+
+ ///
+ /// Helper methods
+ ///
+
+ public function getServerHost() : string {
+ return self::extractHost($_SERVER['HTTP_HOST']);
+ }
+
+ /**
+ * Extracts host from a host:port expression
+ */
+ private static function extractHost (string $expression) : string {
+ $position = strpos($expression, ':');
+
+ if ($position === false) {
+ return $expression;
+ }
+
+ return substr ($expression, 0, $position);
+ }
+
+ /**
+ * @throws SaasException when no URL server parameter have been found.
+ */
+ public function getUri() : string {
+ $sources = [
+ 'DOCUMENT_URI',
+ 'REQUEST_URI',
+ ];
+
+ foreach ($sources as $source) {
+ if (isset($_SERVER[$source])) {
+ return $_SERVER[$source];
+ }
+ }
+
+ throw new SaasException("Can't get URI.");
+ }
+
+ /**
+ * @throws SaasException where the URI can't be found
+ */
+ private function isAliveRequest() : bool {
+ return
+ $_SERVER['REQUEST_METHOD'] === 'GET'
+ &&
+ $this->getUri() === '/status';
+ }
+
+}
Index: src/Configuration.php
===================================================================
--- /dev/null
+++ src/Configuration.php
@@ -0,0 +1,65 @@
+<?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;
+ }
+
+ /**
+ * @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/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,40 @@
+<?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 getDatabaseFromHost (string $host) : string {
+ // Case 1 - the host is canonical
+ $canonicalList = static::getList();
+ if (isset($canonicalList[$host])) {
+ return $canonicalList[$host];
+ }
+
+ // Case 2 - the host is an alias
+ foreach (static::getAliases() as $database => $vhost) {
+ if ($host === $vhost) {
+ return $database;
+ }
+ }
+
+ throw new InstanceNotFoundException($host);
+ }
+
+}
Index: src/Service.php
===================================================================
--- /dev/null
+++ src/Service.php
@@ -0,0 +1,101 @@
+<?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;
+
+class Service extends BaseService {
+
+ /**
+ * @var string
+ */
+ private $host;
+
+ /**
+ * @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
+ ->handleNotExistingSite()
+ ->handleAliveRequest();
+ } catch (SaaSException) {
+ 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-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 function getEntryPoint() : string {
+ return $_ENV['MEDIAWIKI_ENTRY_POINT'];
+ }
+
+ public function getConfiguration() : Configuration {
+ if ($this->configuration === null) {
+ $this->configuration = new Configuration($this->host);
+ }
+
+ return $this->configuration;
+ }
+
+}
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 wiki. If you're creating a new wiki, this message means
+ you've successfully configured the DNS and the front-end web server, and the next step
+ is to declare your wiki to the saas-mediawiki repository.
+</p>
+<p>Create the wiki database if still not done and
+ declare the host / database pair to the config/Instances.php file.</p>
+<p>This repository is also where you can customize the wiki settings,
+in the config/Settings.php file.</p>
+<p>You've done all those steps? Reload php-fpm or troubleshoot those files.</p>
+</html>
\ No newline at end of file
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, 07:55 (20 h, 52 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2262134
Default Alt Text
D1442.id3685.diff (21 KB)

Event Timeline