mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-03-11 09:04:54 +00:00
mediawiki: Use drop-in config component for /etc files
- Don't ship /etc/mediawiki/FreedomBoxSettings.php anymore. Create the file on first setup. Keep old file on update. - Simplify and unify how the configuration settings are read and written. Tests: - Run unit and functional tests. - All the drop-in config files in /etc/ are symlinks. - Shipped configuration is effective. - Upgrade from older version keeps old configuration. - Config files are all symlinks in /etc/ - When upgrading from older version FreedomBoxSettings.php does not change. FreedomBoxStaticSettings.php becomes a symlink. Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
parent
c326b35238
commit
cd512bd24c
2
debian/freedombox.maintscript
vendored
2
debian/freedombox.maintscript
vendored
@ -117,3 +117,5 @@ rm_conffile /etc/letsencrypt/renewal-hooks/deploy/50-freedombox 23.10~
|
||||
rm_conffile /etc/apache2/conf-available/matrix-synapse-plinth.conf 23.10~
|
||||
rm_conffile /etc/fail2ban/jail.d/matrix-auth-freedombox.conf 23.10~
|
||||
rm_conffile /etc/fail2ban/filter.d/matrix-auth-freedombox.conf 23.10~
|
||||
rm_conffile /etc/apache2/conf-available/mediawiki-freedombox.conf 23.10~
|
||||
rm_conffile /etc/mediawiki/FreedomBoxStaticSettings.php 23.10~
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""FreedomBox app to configure MediaWiki."""
|
||||
|
||||
import re
|
||||
import pathlib
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from plinth import app as app_module
|
||||
from plinth import frontpage, menu
|
||||
from plinth.config import DropinConfigs
|
||||
from plinth.daemon import Daemon
|
||||
from plinth.modules.apache.components import Webserver
|
||||
from plinth.modules.backups.components import BackupRestore
|
||||
@ -31,7 +32,6 @@ _description = [
|
||||
'logged in can make changes to the content.')
|
||||
]
|
||||
|
||||
STATIC_CONFIG_FILE = '/etc/mediawiki/FreedomBoxStaticSettings.php'
|
||||
USER_CONFIG_FILE = '/etc/mediawiki/FreedomBoxSettings.php'
|
||||
|
||||
|
||||
@ -40,7 +40,7 @@ class MediaWikiApp(app_module.App):
|
||||
|
||||
app_id = 'mediawiki'
|
||||
|
||||
_version = 10
|
||||
_version = 11
|
||||
|
||||
def __init__(self):
|
||||
"""Create components for the app."""
|
||||
@ -70,6 +70,12 @@ class MediaWikiApp(app_module.App):
|
||||
['mediawiki', 'imagemagick', 'php-sqlite3'])
|
||||
self.add(packages)
|
||||
|
||||
dropin_configs = DropinConfigs('dropin-configs-mediawiki', [
|
||||
'/etc/apache2/conf-available/mediawiki-freedombox.conf',
|
||||
'/etc/mediawiki/FreedomBoxStaticSettings.php',
|
||||
])
|
||||
self.add(dropin_configs)
|
||||
|
||||
firewall = Firewall('firewall-mediawiki', info.name,
|
||||
ports=['http', 'https'], is_external=True)
|
||||
self.add(firewall)
|
||||
@ -109,7 +115,7 @@ class Shortcut(frontpage.Shortcut):
|
||||
def enable(self):
|
||||
"""When enabled, check if MediaWiki is in private mode."""
|
||||
super().enable()
|
||||
self.login_required = privileged.private_mode('status')
|
||||
self.login_required = get_config()['enable_private_mode']
|
||||
|
||||
|
||||
def _get_config_value_in_file(setting_name, config_file):
|
||||
@ -117,26 +123,46 @@ def _get_config_value_in_file(setting_name, config_file):
|
||||
with open(config_file, 'r', encoding='utf-8') as config:
|
||||
for line in config:
|
||||
if line.startswith(setting_name):
|
||||
return re.findall(r'["\'][^"\']*["\']', line)[0].strip('"\'')
|
||||
return line.partition('=')[2].strip('\n ;\'"')
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _get_static_config_file():
|
||||
"""Return the path for the file containing static settings."""
|
||||
base_path = ('/usr/share/freedombox/etc/'
|
||||
'mediawiki/FreedomBoxStaticSettings.php')
|
||||
for path in [
|
||||
pathlib.Path(base_path),
|
||||
pathlib.Path(__file__).parent / 'data' / base_path.lstrip('/')
|
||||
]:
|
||||
if path.exists():
|
||||
return path
|
||||
|
||||
raise RuntimeError('Unable to find static config file')
|
||||
|
||||
|
||||
def _get_config_value(setting_name):
|
||||
"""Return a configuration value from multiple configuration files."""
|
||||
return _get_config_value_in_file(setting_name, USER_CONFIG_FILE) or \
|
||||
_get_config_value_in_file(setting_name, STATIC_CONFIG_FILE)
|
||||
_get_config_value_in_file(setting_name, _get_static_config_file())
|
||||
|
||||
|
||||
def get_default_skin():
|
||||
"""Return the value of the default skin."""
|
||||
return _get_config_value('$wgDefaultSkin')
|
||||
|
||||
|
||||
def get_server_url():
|
||||
"""Return the value of the server URL."""
|
||||
def get_config():
|
||||
"""Return all the configuration settings."""
|
||||
server_url = _get_config_value('$wgServer')
|
||||
return urlparse(server_url).netloc
|
||||
create_permission = _get_config_value(
|
||||
"$wgGroupPermissions['*']['createaccount']")
|
||||
read_permission = _get_config_value("$wgGroupPermissions['*']['read']")
|
||||
print('=====', create_permission, read_permission)
|
||||
return {
|
||||
'default_skin': _get_config_value('$wgDefaultSkin'),
|
||||
'domain': urlparse(server_url).netloc,
|
||||
'site_name': _get_config_value('$wgSitename') or 'Wiki',
|
||||
'default_lang': _get_config_value('$wgLanguageCode') or None,
|
||||
'enable_public_registrations': 'true' in create_permission,
|
||||
'enable_private_mode': 'false' in read_permission,
|
||||
}
|
||||
|
||||
|
||||
def set_server_url(domain):
|
||||
@ -146,13 +172,3 @@ def set_server_url(domain):
|
||||
protocol = 'http'
|
||||
|
||||
privileged.set_server_url(f'{protocol}://{domain}')
|
||||
|
||||
|
||||
def get_site_name():
|
||||
"""Return the value of MediaWiki's site name."""
|
||||
return _get_config_value('$wgSitename') or 'Wiki'
|
||||
|
||||
|
||||
def get_default_language():
|
||||
"""Return the value of MediaWiki's default language"""
|
||||
return _get_config_value('$wgLanguageCode') or None
|
||||
|
||||
@ -1,26 +0,0 @@
|
||||
<?php
|
||||
|
||||
# Default logo
|
||||
$wgLogo = "$wgResourceBasePath/resources/assets/mediawiki.png";
|
||||
|
||||
# Enable file uploads
|
||||
$wgEnableUploads = true;
|
||||
|
||||
# Public registrations
|
||||
$wgGroupPermissions['*']['createaccount'] = false;
|
||||
|
||||
# Read/write permissions for anonymous users
|
||||
$wgGroupPermissions['*']['edit'] = false;
|
||||
$wgGroupPermissions['*']['read'] = true;
|
||||
|
||||
# Short urls
|
||||
$wgArticlePath = "/mediawiki/$1";
|
||||
$wgUsePathInfo = true;
|
||||
|
||||
# Instant Commons
|
||||
$wgUseInstantCommons = true;
|
||||
|
||||
# SVG Enablement
|
||||
$wgFileExtensions[] = 'svg';
|
||||
$wgAllowTitlesInSVG = true;
|
||||
$wgSVGConverter = 'ImageMagick';
|
||||
@ -3,9 +3,8 @@
|
||||
This file is shipped by FreedomBox to manage static settings. Newer versions of
|
||||
this file are shipped by FreedomBox and are expected to override this file
|
||||
without any user prompts. It should not be modified by the system
|
||||
administrator. Additional setting modified by FreedomBox at placed in
|
||||
FreedomBoxSettings.php. No newer version of that file is ever shipped by
|
||||
FreedomBox.
|
||||
administrator. Additional setting modified by FreedomBox are placed in
|
||||
FreedomBoxSettings.php.
|
||||
*/
|
||||
|
||||
# Default logo
|
||||
@ -6,7 +6,6 @@ import pathlib
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
from typing import Optional
|
||||
|
||||
from plinth.actions import privileged
|
||||
from plinth.utils import generate_password
|
||||
@ -60,6 +59,11 @@ def setup():
|
||||
])
|
||||
subprocess.run(['chmod', '-R', 'o-rwx', data_dir], check=True)
|
||||
subprocess.run(['chown', '-R', 'www-data:www-data', data_dir], check=True)
|
||||
|
||||
conf_file = pathlib.Path(CONF_FILE)
|
||||
if not conf_file.exists():
|
||||
conf_file.write_text('<?php\n')
|
||||
|
||||
_include_custom_config()
|
||||
|
||||
|
||||
@ -109,68 +113,6 @@ def update():
|
||||
subprocess.check_call([get_php_command(), update_script, '--quick'])
|
||||
|
||||
|
||||
@privileged
|
||||
def public_registrations(command: str) -> Optional[bool]:
|
||||
"""Enable or Disable public registrations for MediaWiki."""
|
||||
if command not in ('enable', 'disable', 'status'):
|
||||
raise ValueError('Invalid command')
|
||||
|
||||
with open(CONF_FILE, 'r', encoding='utf-8') as conf_file:
|
||||
lines = conf_file.readlines()
|
||||
|
||||
def is_pub_reg_line(line):
|
||||
return line.startswith("$wgGroupPermissions['*']['createaccount']")
|
||||
|
||||
if command == 'status':
|
||||
conf_lines = list(filter(is_pub_reg_line, lines))
|
||||
return bool(conf_lines and 'true' in conf_lines[0])
|
||||
|
||||
with open(CONF_FILE, 'w', encoding='utf-8') as conf_file:
|
||||
for line in lines:
|
||||
if is_pub_reg_line(line):
|
||||
words = line.split()
|
||||
if command == 'enable':
|
||||
words[-1] = 'true;'
|
||||
else:
|
||||
words[-1] = 'false;'
|
||||
conf_file.write(" ".join(words) + '\n')
|
||||
else:
|
||||
conf_file.write(line)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
@privileged
|
||||
def private_mode(command: str):
|
||||
"""Enable or Disable Private mode for wiki."""
|
||||
if command not in ('enable', 'disable', 'status'):
|
||||
raise ValueError('Invalid command')
|
||||
|
||||
with open(CONF_FILE, 'r', encoding='utf-8') as conf_file:
|
||||
lines = conf_file.readlines()
|
||||
|
||||
def is_read_line(line):
|
||||
return line.startswith("$wgGroupPermissions['*']['read']")
|
||||
|
||||
read_conf_lines = list(filter(is_read_line, lines))
|
||||
if command == 'status':
|
||||
return (read_conf_lines and 'false' in read_conf_lines[0])
|
||||
|
||||
with open(CONF_FILE, 'w', encoding='utf-8') as conf_file:
|
||||
conf_value = 'false;' if command == 'enable' else 'true;'
|
||||
for line in lines:
|
||||
if is_read_line(line):
|
||||
words = line.split()
|
||||
words[-1] = conf_value
|
||||
conf_file.write(" ".join(words) + '\n')
|
||||
else:
|
||||
conf_file.write(line)
|
||||
|
||||
if not read_conf_lines:
|
||||
conf_file.write("$wgGroupPermissions['*']['read'] = " +
|
||||
conf_value + '\n')
|
||||
|
||||
|
||||
def _update_setting(setting_name, setting_line):
|
||||
"""Update the value of one setting in the config file."""
|
||||
with open(CONF_FILE, 'r', encoding='utf-8') as conf_file:
|
||||
@ -190,10 +132,26 @@ def _update_setting(setting_name, setting_line):
|
||||
conf_file.writelines(lines)
|
||||
|
||||
|
||||
@privileged
|
||||
def set_public_registrations(should_enable: bool):
|
||||
"""Enable or Disable public registrations for MediaWiki."""
|
||||
setting = "$wgGroupPermissions['*']['createaccount']"
|
||||
conf_value = 'true' if should_enable else 'false'
|
||||
_update_setting(setting, f'{setting} = {conf_value};\n')
|
||||
|
||||
|
||||
@privileged
|
||||
def set_private_mode(should_enable: bool):
|
||||
"""Enable or Disable Private mode for wiki."""
|
||||
setting = "$wgGroupPermissions['*']['read']"
|
||||
conf_value = 'false' if should_enable else 'true'
|
||||
_update_setting(setting, f'{setting} = {conf_value};\n')
|
||||
|
||||
|
||||
@privileged
|
||||
def set_default_skin(skin: str):
|
||||
"""Set a default skin."""
|
||||
_update_setting('$wgDefaultSkin ', f'$wgDefaultSkin = "{skin}";\n')
|
||||
_update_setting('$wgDefaultSkin', f'$wgDefaultSkin = "{skin}";\n')
|
||||
|
||||
|
||||
@privileged
|
||||
@ -228,3 +186,4 @@ def uninstall():
|
||||
"""Remove Mediawiki's database and local config file."""
|
||||
shutil.rmtree('/var/lib/mediawiki-db', ignore_errors=True)
|
||||
pathlib.Path(LOCAL_SETTINGS_CONF).unlink(missing_ok=True)
|
||||
pathlib.Path(CONF_FILE).unlink(missing_ok=True)
|
||||
|
||||
@ -4,7 +4,6 @@ Test module for MediaWiki utility functions.
|
||||
"""
|
||||
|
||||
import pathlib
|
||||
import shutil
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
@ -17,60 +16,57 @@ current_directory = pathlib.Path(__file__).parent
|
||||
privileged_modules_to_mock = ['plinth.modules.mediawiki.privileged']
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def fixture_setup_configuration(conf_file):
|
||||
"""Set configuration file path in actions module."""
|
||||
privileged.CONF_FILE = conf_file
|
||||
@pytest.fixture(name='test_configuration', autouse=True)
|
||||
def fixture_test_configuration(tmp_path):
|
||||
"""Use a separate MediaWiki configuration for tests.
|
||||
|
||||
|
||||
@pytest.fixture(name='conf_file')
|
||||
def fixture_conf_file(tmp_path):
|
||||
"""Uses a dummy configuration file."""
|
||||
FreedomBoxStaticSettings.php is used read-only from source code location.
|
||||
"""
|
||||
settings_file_name = 'FreedomBoxSettings.php'
|
||||
conf_file = tmp_path / settings_file_name
|
||||
conf_file.touch()
|
||||
shutil.copyfile(
|
||||
str(current_directory / '..' / 'data' / 'etc' / 'mediawiki' /
|
||||
settings_file_name), str(conf_file))
|
||||
return str(conf_file)
|
||||
|
||||
|
||||
@pytest.fixture(name='test_configuration', autouse=True)
|
||||
def fixture_test_configuration(conf_file):
|
||||
"""Use a separate MediaWiki configuration for tests.
|
||||
|
||||
Uses local FreedomBoxStaticSettings.php, a temp version of
|
||||
FreedomBoxSettings.php
|
||||
|
||||
"""
|
||||
data_directory = pathlib.Path(__file__).parent.parent / 'data'
|
||||
static_config_file = str(data_directory / 'etc' / 'mediawiki' /
|
||||
mediawiki.STATIC_CONFIG_FILE.split('/')[-1])
|
||||
with patch('plinth.modules.mediawiki.STATIC_CONFIG_FILE',
|
||||
static_config_file), \
|
||||
patch('plinth.modules.mediawiki.USER_CONFIG_FILE', conf_file):
|
||||
with (patch('plinth.modules.mediawiki.USER_CONFIG_FILE', conf_file),
|
||||
patch('plinth.modules.mediawiki.privileged.CONF_FILE', conf_file)):
|
||||
yield
|
||||
|
||||
|
||||
def test_private_mode():
|
||||
"""Test enabling/disabling private mode."""
|
||||
assert not mediawiki.get_config()['enable_private_mode']
|
||||
privileged.set_private_mode(True)
|
||||
assert mediawiki.get_config()['enable_private_mode']
|
||||
privileged.set_private_mode(False)
|
||||
assert not mediawiki.get_config()['enable_private_mode']
|
||||
|
||||
|
||||
def test_public_registrations():
|
||||
"""Test enabling/disabling public registrations."""
|
||||
assert not mediawiki.get_config()['enable_public_registrations']
|
||||
privileged.set_public_registrations(True)
|
||||
assert mediawiki.get_config()['enable_public_registrations']
|
||||
privileged.set_public_registrations(False)
|
||||
assert not mediawiki.get_config()['enable_public_registrations']
|
||||
|
||||
|
||||
def test_default_skin():
|
||||
"""Test getting and setting the default skin."""
|
||||
assert mediawiki.get_default_skin() == 'timeless'
|
||||
assert mediawiki.get_config()['default_skin'] == 'timeless'
|
||||
new_skin = 'vector'
|
||||
privileged.set_default_skin(new_skin)
|
||||
assert mediawiki.get_default_skin() == new_skin
|
||||
assert mediawiki.get_config()['default_skin'] == new_skin
|
||||
|
||||
|
||||
def test_server_url():
|
||||
def test_domain():
|
||||
"""Test getting and setting $wgServer."""
|
||||
assert mediawiki.get_server_url() == 'freedombox.local'
|
||||
assert mediawiki.get_config()['domain'] == 'freedombox.local'
|
||||
new_domain = 'mydomain.freedombox.rocks'
|
||||
mediawiki.set_server_url(new_domain)
|
||||
assert mediawiki.get_server_url() == new_domain
|
||||
assert mediawiki.get_config()['domain'] == new_domain
|
||||
|
||||
|
||||
def test_site_name():
|
||||
"""Test getting and setting $wgSitename."""
|
||||
assert mediawiki.get_site_name() == 'Wiki'
|
||||
assert mediawiki.get_config()['site_name'] == 'Wiki'
|
||||
new_site_name = 'My MediaWiki'
|
||||
privileged.set_site_name(new_site_name)
|
||||
assert mediawiki.get_site_name() == new_site_name
|
||||
assert mediawiki.get_config()['site_name'] == new_site_name
|
||||
|
||||
@ -10,8 +10,7 @@ from plinth import app as app_module
|
||||
from plinth import views
|
||||
from plinth.modules import mediawiki
|
||||
|
||||
from . import (get_default_skin,
|
||||
get_server_url, get_site_name, get_default_language, privileged)
|
||||
from . import privileged
|
||||
from .forms import MediaWikiForm
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -27,20 +26,7 @@ class MediaWikiAppView(views.AppView):
|
||||
def get_initial(self):
|
||||
"""Return the values to fill in the form."""
|
||||
initial = super().get_initial()
|
||||
initial.update({
|
||||
'enable_public_registrations':
|
||||
privileged.public_registrations('status'),
|
||||
'enable_private_mode':
|
||||
privileged.private_mode('status'),
|
||||
'default_skin':
|
||||
get_default_skin(),
|
||||
'domain':
|
||||
get_server_url(),
|
||||
'site_name':
|
||||
get_site_name(),
|
||||
'default_lang':
|
||||
get_default_language()
|
||||
})
|
||||
initial.update(mediawiki.get_config())
|
||||
return initial
|
||||
|
||||
def form_valid(self, form):
|
||||
@ -66,7 +52,7 @@ class MediaWikiAppView(views.AppView):
|
||||
# note action public-registration restarts, if running now
|
||||
if new_config['enable_public_registrations']:
|
||||
if not new_config['enable_private_mode']:
|
||||
privileged.public_registrations('enable')
|
||||
privileged.set_public_registrations(True)
|
||||
messages.success(self.request,
|
||||
_('Public registrations enabled'))
|
||||
else:
|
||||
@ -74,19 +60,19 @@ class MediaWikiAppView(views.AppView):
|
||||
self.request, 'Public registrations ' +
|
||||
'cannot be enabled when private mode is enabled')
|
||||
else:
|
||||
privileged.public_registrations('disable')
|
||||
privileged.set_public_registrations(False)
|
||||
messages.success(self.request,
|
||||
_('Public registrations disabled'))
|
||||
|
||||
if is_changed('enable_private_mode'):
|
||||
if new_config['enable_private_mode']:
|
||||
privileged.private_mode('enable')
|
||||
privileged.set_private_mode(True)
|
||||
messages.success(self.request, _('Private mode enabled'))
|
||||
if new_config['enable_public_registrations']:
|
||||
# If public registrations are enabled, then disable it
|
||||
privileged.public_registrations('disable')
|
||||
privileged.set_public_registrations(False)
|
||||
else:
|
||||
privileged.private_mode('disable')
|
||||
privileged.set_private_mode(False)
|
||||
messages.success(self.request, _('Private mode disabled'))
|
||||
|
||||
app = app_module.App.get('mediawiki')
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user