Page Menu
Home
DevCentral
Search
Configure Global Search
Log In
Files
F11722584
D1442.id3683.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
15 KB
Referenced Files
None
Subscribers
None
D1442.id3683.diff
View Options
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,28 @@
+{
+ "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\\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,37 @@
+<?php
+
+namespace Nasqueron\SAAS\MediaWiki\Configuration;
+
+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/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,35 @@
+<?php
+
+namespace Nasqueron\SAAS\MediaWiki;
+
+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,139 @@
+<?php
+
+namespace Nasqueron\SAAS\MediaWiki;
+
+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();
+ }
+
+ 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 '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;
+ }
+
+}
+
+abstract class BaseService {
+
+ ///
+ /// Request methods
+ ///
+
+ public function handleNotExistingSite() : BaseService {
+ if (!$this->isExisting()) {
+ $this->serveNotExistingResponse();
+ }
+
+ return $this;
+ }
+
+ public function handleAliveRequest() : BaseService {
+ // 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 hosts from a host:port expression
+ */
+ private static function extractHost ($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';
+ }
+
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Sep 17, 03:40 (21 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2989937
Default Alt Text
D1442.id3683.diff (15 KB)
Attached To
Mode
D1442: Provide configuration for wikis
Attached
Detach File
Event Timeline
Log In to Comment