Page MenuHomeDevCentral

D1442.id3684.diff
No OneTemporary

D1442.id3684.diff

diff --git a/.env.example b/.env.example
new file mode 100644
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,4 @@
+MEDIAWIKI_ENTRY_POINT="/srv/mediawiki/index.php"
+DB_HOST="localhost"
+DB_USER="root"
+DB_PASS=""
diff --git a/.gitignore b/.gitignore
new file mode 100644
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+# Composer
+/vendor/
+composer.lock
+
+# DotEnv
+.env
diff --git a/LocalSettings.php b/LocalSettings.php
new file mode 100644
--- /dev/null
+++ b/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::onSitePameters';
+
+$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'));
diff --git a/composer.json b/composer.json
new file mode 100644
--- /dev/null
+++ b/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/"
+ }
+ }
+}
diff --git a/config/CommonSettings.php b/config/CommonSettings.php
new file mode 100644
--- /dev/null
+++ b/config/CommonSettings.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Nasqueron\SAAS\MediaWiki\Configuration;
+
+use Nasqueron\SAAS\ConfigurationException;
+
+class CommonSettings {
+
+ public static function mapSettings (array &$settings) : void {
+ $settings += self::getMappedSettings($settings);
+ }
+
+ public static function getMappedSettings (array $settings) : array {
+ $mappedSettings = [];
+ $mappedSettings += self::getRightsSettings($settings['saasLicense']);
+ return $mappedSettings;
+ }
+
+ ///
+ /// Individual set of settings
+ ///
+
+ 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;
+ }
+}
diff --git a/config/Instances.php b/config/Instances.php
new file mode 100644
--- /dev/null
+++ b/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",
+ ]
+ ];
+ }
+
+}
diff --git a/config/MappableSettings.php b/config/MappableSettings.php
new file mode 100644
--- /dev/null
+++ b/config/MappableSettings.php
@@ -0,0 +1,43 @@
+<?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 reml databases.
+
+class MappableSettings {
+
+ 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;
+ }
+
+}
diff --git a/config/Settings.php b/config/Settings.php
new file mode 100644
--- /dev/null
+++ b/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 protected 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,
+ ],
+ ];
+ }
+
+}
diff --git a/index.php b/index.php
new file mode 100644
--- /dev/null
+++ b/index.php
@@ -0,0 +1,14 @@
+<?php
+
+use Nasqueron\SAAS\MediaWiki\Service;
+
+// Composer PSR-4 autoloading and .env → environment
+require __DIR__ . '/vendor/autoload.php';
+(new Dotenv\Dotenv(__DIR__))->load();
+
+$service = new Service();
+$service
+ ->handleNotExistingSite()
+ ->handleAliveRequest();
+
+require $service->getEntryPoint();
diff --git a/lib-to-move-to-saas-service/ConfigurationException.php b/lib-to-move-to-saas-service/ConfigurationException.php
new file mode 100644
--- /dev/null
+++ b/lib-to-move-to-saas-service/ConfigurationException.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace Nasqueron\SAAS;
+
+
+class ConfigurationException extends \Exception {
+
+}
diff --git a/lib-to-move-to-saas-service/InstanceNotFoundException.php b/lib-to-move-to-saas-service/InstanceNotFoundException.php
new file mode 100644
--- /dev/null
+++ b/lib-to-move-to-saas-service/InstanceNotFoundException.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace Nasqueron\SAAS;
+
+
+class InstanceNotFoundException extends \Exception {
+
+}
diff --git a/lib-to-move-to-saas-service/Service.php b/lib-to-move-to-saas-service/Service.php
new file mode 100644
--- /dev/null
+++ b/lib-to-move-to-saas-service/Service.php
@@ -0,0 +1,91 @@
+<?php
+
+namespace Nasqueron\SAAS;
+
+abstract class Service {
+
+ ///
+ /// Request methods
+ ///
+
+ public function handleNotExistingSite() : Service {
+ if (!$this->isExisting()) {
+ $this->serveNotExistingResponse();
+ }
+
+ return $this;
+ }
+
+ 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 $host) : string {
+ $pos = strpos($host, ':');
+
+ if ($pos === false) {
+ return $host;
+ }
+
+ return substr ($host, 0, $pos);
+ }
+
+ public function getUri() : string {
+ $sources = [
+ 'DOCUMENT_URI',
+ 'REQUEST_URI',
+ ];
+
+ foreach ($sources as $source) {
+ if (isset($_SERVER[$source])) {
+ return $_SERVER[$source];
+ }
+ }
+
+ throw new \Exception("Can't get URI.");
+ }
+
+ private function isAliveRequest() : bool {
+ return
+ $_SERVER['REQUEST_METHOD'] === 'GET'
+ &&
+ $this->getUri() === '/status';
+ }
+
+}
diff --git a/src/Configuration.php b/src/Configuration.php
new file mode 100644
--- /dev/null
+++ b/src/Configuration.php
@@ -0,0 +1,62 @@
+<?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 getSelectedDatabase () {
+ return Instances::getDatabaseFromHost($this->host);
+ }
+
+ ///
+ /// Helper methods
+ ///
+
+ public static function isSelectedWiki (string $wiki, string $suffix) : bool {
+ return substr($wiki, -strlen($suffix)) == $suffix;
+ }
+}
diff --git a/src/Hooks.php b/src/Hooks.php
new file mode 100644
--- /dev/null
+++ b/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' => [],
+ ];
+ }
+
+}
diff --git a/src/InstancesRepository.php b/src/InstancesRepository.php
new file mode 100644
--- /dev/null
+++ b/src/InstancesRepository.php
@@ -0,0 +1,37 @@
+<?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
+ ///
+
+ public static function getDatabaseFromHost (string $host) {
+ // 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);
+ }
+
+}
diff --git a/src/Service.php b/src/Service.php
new file mode 100644
--- /dev/null
+++ b/src/Service.php
@@ -0,0 +1,67 @@
+<?php
+
+namespace Nasqueron\SAAS\MediaWiki;
+
+use Nasqueron\SAAS\InstanceNotFoundException;
+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;
+
+ public function __construct (string $host = '') {
+ $this->host = $host ?: self::getServerHost();
+ }
+
+ 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 getEntryPoint() : string {
+ return $_ENV['MEDIAWIKI_ENTRY_POINT'];
+ }
+
+ public function getConfiguration() : Configuration {
+ if ($this->configuration === null) {
+ $this->configuration = new Configuration($this->host);
+ }
+
+ return $this->configuration;
+ }
+
+}
diff --git a/views/404.php b/views/404.php
new file mode 100644
--- /dev/null
+++ b/views/404.php
@@ -0,0 +1,18 @@
+<!doctype html>
+<html class="no-js" lang="">
+<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>
+<!--[if lte IE 9]>
+<p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a href="https://browsehappy.com/">upgrade your browser</a> to improve your experience and security.</p>
+<![endif]-->
+
+<!-- Add your site or application content here -->
+<p>The <?= $this->getHost() ?> instance of your service is not available.</p>
+
+</body>
+</html>

File Metadata

Mime Type
text/plain
Expires
Fri, Nov 29, 01:14 (21 h, 37 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2270363
Default Alt Text
D1442.id3684.diff (17 KB)

Event Timeline