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)

Reference Links