MediaWiki
Collaboration Tool
MediaWiki
Overview
MediaWiki is the powerful, flexible open-source wiki software that powers Wikipedia. Written in PHP, it supports a wide range of use cases from large collaborative projects to small internal documentation management. With robust editing features, version control, permission management, and multilingual support, it's adopted by organizations worldwide as a knowledge-sharing platform.
Details
MediaWiki is a scalable and highly customizable wiki platform. It supports both wikitext markup and visual editing, providing comprehensive features including page history, diff viewing, watchlists, and a category system. The extension system allows adding advanced functionality like Semantic MediaWiki, VisualEditor, and Parsoid. It provides both Action API and REST API for programmatic access and integration, making it suitable for various documentation and collaboration needs.
Key Features
- Flexible Editing: Wiki markup and visual editor support
- Version Control: Complete edit history and diff viewing
- Permission System: Fine-grained access control and user group management
- Multilingual Support: Supports over 300 languages
- Search Functionality: Advanced full-text search and category system
- Extensibility: Thousands of extensions for added functionality
- APIs: Action API and REST API for programmatic access
- Scalability: Suitable from small to ultra-large scale deployments
API Overview
Action API
POST https://example.com/w/api.php
Content-Type: application/x-www-form-urlencoded
action=query&meta=siteinfo&siprop=general&format=json
REST API
GET https://example.com/w/rest.php/v1/page/Main_Page
Accept: application/json
Authentication
# Login
POST /w/api.php
action=login&lgname=username&lgpassword=password&format=json
# Get token
POST /w/api.php
action=query&meta=tokens&type=csrf&format=json
Advantages and Disadvantages
Advantages
- Completely free and open source
- Proven robustness (battle-tested by Wikipedia)
- Highly scalable architecture
- Rich extension ecosystem
- Powerful API support
- Comprehensive version control
- Excellent multilingual capabilities
- Large community support
Disadvantages
- Complex initial setup
- Proprietary wiki markup language
- UI may feel dated
- Requires performance tuning
- Extension compatibility management needed
- No real-time editing by default
Practical Examples
1. MediaWiki Docker Setup
version: '3'
services:
mediawiki:
image: mediawiki:latest
restart: always
ports:
- 8080:80
links:
- database
volumes:
- ./images:/var/www/html/images
- ./LocalSettings.php:/var/www/html/LocalSettings.php
environment:
- MEDIAWIKI_DB_HOST=database
- MEDIAWIKI_DB_NAME=mediawiki
- MEDIAWIKI_DB_USER=wikiuser
- MEDIAWIKI_DB_PASSWORD=wikipass
database:
image: mariadb:10.6
restart: always
environment:
- MYSQL_DATABASE=mediawiki
- MYSQL_USER=wikiuser
- MYSQL_PASSWORD=wikipass
- MYSQL_ROOT_PASSWORD=rootpass
volumes:
- ./mysql:/var/lib/mysql
2. Page Operations Using API (Python)
import requests
import json
class MediaWikiAPI:
def __init__(self, api_url, username=None, password=None):
self.api_url = api_url
self.session = requests.Session()
if username and password:
self.login(username, password)
def login(self, username, password):
"""Login to MediaWiki"""
# Get login token
login_token = self.get_token('login')
# Login
login_data = {
'action': 'login',
'lgname': username,
'lgpassword': password,
'lgtoken': login_token,
'format': 'json'
}
response = self.session.post(self.api_url, data=login_data)
return response.json()
def get_token(self, token_type='csrf'):
"""Get API token"""
params = {
'action': 'query',
'meta': 'tokens',
'type': token_type,
'format': 'json'
}
response = self.session.get(self.api_url, params=params)
data = response.json()
if token_type == 'login':
return data['query']['tokens']['logintoken']
else:
return data['query']['tokens']['csrftoken']
def create_page(self, title, content, summary=''):
"""Create or update a page"""
token = self.get_token()
edit_data = {
'action': 'edit',
'title': title,
'text': content,
'summary': summary,
'token': token,
'format': 'json'
}
response = self.session.post(self.api_url, data=edit_data)
return response.json()
def get_page_content(self, title):
"""Get page content"""
params = {
'action': 'query',
'prop': 'revisions',
'titles': title,
'rvprop': 'content',
'format': 'json'
}
response = self.session.get(self.api_url, params=params)
data = response.json()
pages = data['query']['pages']
for page_id, page_data in pages.items():
if 'revisions' in page_data:
return page_data['revisions'][0]['*']
return None
# Usage example
wiki = MediaWikiAPI('https://example.com/w/api.php', 'username', 'password')
# Create a page
wiki.create_page(
'API_Test_Page',
'== Test Page ==\nThis page was created via API.',
'API test page creation'
)
3. JavaScript API Client
class MediaWikiClient {
constructor(apiUrl) {
this.apiUrl = apiUrl;
}
async request(params) {
const url = new URL(this.apiUrl);
Object.keys(params).forEach(key =>
url.searchParams.append(key, params[key])
);
const response = await fetch(url, {
method: params.action === 'edit' ? 'POST' : 'GET',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: params.action === 'edit' ?
new URLSearchParams(params) : undefined
});
return response.json();
}
async searchPages(searchTerm, limit = 10) {
const params = {
action: 'query',
list: 'search',
srsearch: searchTerm,
srlimit: limit,
format: 'json',
origin: '*'
};
const data = await this.request(params);
return data.query.search;
}
async getRecentChanges(limit = 50) {
const params = {
action: 'query',
list: 'recentchanges',
rcprop: 'title|timestamp|user|comment',
rclimit: limit,
format: 'json',
origin: '*'
};
const data = await this.request(params);
return data.query.recentchanges;
}
async getCategoryMembers(category, limit = 100) {
const params = {
action: 'query',
list: 'categorymembers',
cmtitle: `Category:${category}`,
cmlimit: limit,
format: 'json',
origin: '*'
};
const data = await this.request(params);
return data.query.categorymembers;
}
}
// Usage example
const wiki = new MediaWikiClient('https://example.com/w/api.php');
// Search pages
wiki.searchPages('JavaScript').then(results => {
results.forEach(page => {
console.log(`${page.title}: ${page.snippet}`);
});
});
4. Creating Custom Extensions (PHP)
<?php
/**
* CustomTools Extension
*/
class CustomToolsHooks {
/**
* Register parser functions
*/
public static function onParserFirstCallInit(Parser $parser) {
// Add custom parser function
$parser->setFunctionHook('custombox', [self::class, 'renderCustomBox']);
return true;
}
/**
* Render custom box
*/
public static function renderCustomBox(Parser $parser, $content = '', $type = 'info') {
$validTypes = ['info', 'warning', 'success', 'error'];
if (!in_array($type, $validTypes)) {
$type = 'info';
}
$output = Html::rawElement(
'div',
['class' => "custom-box custom-box-$type"],
$parser->recursiveTagParse($content)
);
return [$output, 'noparse' => false];
}
/**
* Add custom API module
*/
public static function onApiMain_moduleManager($moduleManager) {
$moduleManager->addModule(
'customdata',
'action',
'ApiCustomData'
);
return true;
}
}
// extension.json
{
"name": "CustomTools",
"version": "1.0.0",
"author": "Your Name",
"url": "https://example.com/CustomTools",
"description": "Adds custom tools and parser functions",
"license-name": "GPL-2.0-or-later",
"type": "parserhook",
"Hooks": {
"ParserFirstCallInit": "CustomToolsHooks::onParserFirstCallInit",
"ApiMain::moduleManager": "CustomToolsHooks::onApiMain_moduleManager"
},
"ResourceModules": {
"ext.customtools.styles": {
"styles": "resources/ext.customtools.css"
}
},
"ResourceFileModulePaths": {
"localBasePath": "",
"remoteExtPath": "CustomTools"
},
"manifest_version": 2
}
5. Batch Import Script (Python)
#!/usr/bin/env python3
import os
import mwclient
from datetime import datetime
class MediaWikiBatchImporter:
def __init__(self, site_url, path='/w/', username=None, password=None):
self.site = mwclient.Site(site_url, path=path)
if username and password:
self.site.login(username, password)
def import_markdown_files(self, directory, namespace=0):
"""Batch import Markdown files"""
import markdown
md = markdown.Markdown()
imported = []
for root, dirs, files in os.walk(directory):
for file in files:
if file.endswith('.md'):
filepath = os.path.join(root, file)
page_title = file[:-3].replace('_', ' ')
with open(filepath, 'r', encoding='utf-8') as f:
md_content = f.read()
wiki_content = self.markdown_to_wiki(md_content)
try:
page = self.site.pages[page_title]
page.save(
wiki_content,
summary=f'Imported from {file}'
)
imported.append(page_title)
print(f"Imported: {page_title}")
except Exception as e:
print(f"Error importing {page_title}: {e}")
return imported
def markdown_to_wiki(self, md_text):
"""Simple Markdown to Wiki text conversion"""
# Headers
wiki_text = md_text
for i in range(6, 0, -1):
wiki_text = wiki_text.replace(
'#' * i + ' ',
'=' * i + ' '
)
wiki_text = wiki_text.replace(
'\n' + '#' * i + ' ',
'\n' + '=' * i + ' '
)
# Lists
wiki_text = wiki_text.replace('\n- ', '\n* ')
wiki_text = wiki_text.replace('\n - ', '\n** ')
# Links
import re
wiki_text = re.sub(
r'\[([^\]]+)\]\(([^\)]+)\)',
r'[\2 \1]',
wiki_text
)
return wiki_text
def create_category_structure(self, categories):
"""Create category structure"""
for cat_name, cat_info in categories.items():
cat_page = self.site.pages[f'Category:{cat_name}']
content = f"== {cat_info['description']} ==\n"
if 'parent' in cat_info:
content += f"\n[[Category:{cat_info['parent']}]]"
cat_page.save(content, summary='Category creation')
print(f"Created category: {cat_name}")
# Usage example
importer = MediaWikiBatchImporter(
'example.com',
username='ImportBot',
password='bot_password'
)
# Import markdown files
importer.import_markdown_files('/path/to/markdown/docs')
# Create category structure
categories = {
'Documentation': {
'description': 'Main category for technical documentation'
},
'API_Documentation': {
'description': 'API-related documentation',
'parent': 'Documentation'
}
}
importer.create_category_structure(categories)