mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-01-21 07:55:00 +00:00
mediawiki: Add action to set domain name
- Set domain name during app setup - Improve tests for settings. Prefer to call functions in plinth which invoke actions than test actions directly. - Also, '$wgServer' is not a domain name since it also includes the protocol. - Add domain selection form. Make server url a text input field. - Added a functional test to set the value of server url to the value provided by FREEDOMBOX_URL before doing running any other tests. - Make server url setting a pre-requisite. Signed-off-by: Joseph Nuthalapati <njoseph@riseup.net> [sunil: Squash commits as they were fixing themselves] [sunil: Simplify configuration reading] [sunil: Use 'server_url' terminology consistently] [sunil: cosmetic: Minor styling] [sunil: Update test_settings.py to use fixture pattern] [sunil: Remove seemingly incorrectly used aria-describedby attribute] [sunil: Don't rely solely on env variable value in functional tests] [sunil: Fix issue with http/https mismatch when checking site availability] Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: Sunil Mohan Adapa <sunil@medhas.org>
This commit is contained in:
parent
04617cbf7f
commit
658e260d23
@ -48,6 +48,10 @@ def parse_arguments():
|
||||
help='Set the default skin')
|
||||
default_skin.add_argument('skin', help='name of the skin')
|
||||
|
||||
server_url = subparsers.add_parser(
|
||||
'set-server-url', help='Set the value of $wgServer for this server')
|
||||
server_url.add_argument('server_url', help='value of $wgServer')
|
||||
|
||||
subparsers.required = True
|
||||
return parser.parse_args()
|
||||
|
||||
@ -218,28 +222,37 @@ def subcommand_private_mode(arguments):
|
||||
conf_value + '\n')
|
||||
|
||||
|
||||
def subcommand_set_default_skin(arguments):
|
||||
"""Set a default skin"""
|
||||
skin = arguments.skin
|
||||
skin_setting = f'$wgDefaultSkin = "{skin}";\n'
|
||||
|
||||
def _update_setting(setting_name, setting_line):
|
||||
"""Update the value of one setting in the config file."""
|
||||
with open(CONF_FILE, 'r') as conf_file:
|
||||
lines = conf_file.readlines()
|
||||
|
||||
inserted = False
|
||||
for i, line in enumerate(lines):
|
||||
if line.strip().startswith('$wgDefaultSkin'):
|
||||
lines[i] = skin_setting
|
||||
if line.strip().startswith(setting_name):
|
||||
lines[i] = setting_line
|
||||
inserted = True
|
||||
break
|
||||
|
||||
if not inserted:
|
||||
lines.append(skin_setting)
|
||||
lines.append(setting_line)
|
||||
|
||||
with open(CONF_FILE, 'w') as conf_file:
|
||||
conf_file.writelines(lines)
|
||||
|
||||
|
||||
def subcommand_set_default_skin(arguments):
|
||||
"""Set a default skin."""
|
||||
skin = arguments.skin
|
||||
_update_setting('$wgDefaultSkin ', f'$wgDefaultSkin = "{skin}";\n')
|
||||
|
||||
|
||||
def subcommand_set_server_url(arguments):
|
||||
"""Set the value of $wgServer for this MediaWiki server."""
|
||||
# This is a required setting from MediaWiki 1.34
|
||||
_update_setting('$wgServer', f'$wgServer = "{arguments.server_url}";\n')
|
||||
|
||||
|
||||
def main():
|
||||
"""Parse arguments and perform all duties."""
|
||||
arguments = parse_arguments()
|
||||
|
||||
@ -4,6 +4,7 @@ FreedomBox app to configure MediaWiki.
|
||||
"""
|
||||
|
||||
import re
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
@ -16,7 +17,7 @@ from plinth.modules.firewall.components import Firewall
|
||||
|
||||
from .manifest import backup, clients # noqa, pylint: disable=unused-import
|
||||
|
||||
version = 8
|
||||
version = 9
|
||||
|
||||
managed_packages = ['mediawiki', 'imagemagick', 'php-sqlite3']
|
||||
|
||||
@ -39,6 +40,9 @@ _description = [
|
||||
|
||||
app = None
|
||||
|
||||
STATIC_CONFIG_FILE = '/etc/mediawiki/FreedomBoxStaticSettings.php'
|
||||
USER_CONFIG_FILE = '/etc/mediawiki/FreedomBoxSettings.php'
|
||||
|
||||
|
||||
class MediaWikiApp(app_module.App):
|
||||
"""FreedomBox app for MediaWiki."""
|
||||
@ -109,24 +113,44 @@ def is_public_registration_enabled():
|
||||
|
||||
|
||||
def is_private_mode_enabled():
|
||||
""" Return whether private mode is enabled or disabled"""
|
||||
"""Return whether private mode is enabled or disabled."""
|
||||
output = actions.superuser_run('mediawiki', ['private-mode', 'status'])
|
||||
return output.strip() == 'enabled'
|
||||
|
||||
|
||||
def get_default_skin():
|
||||
"""Return the value of the default skin"""
|
||||
|
||||
def _find_skin(config_file):
|
||||
with open(config_file, 'r') as config:
|
||||
for line in config:
|
||||
if line.startswith('$wgDefaultSkin'):
|
||||
return re.findall(r'["\'][^"\']*["\']',
|
||||
line)[0].strip('"\'')
|
||||
def _get_config_value_in_file(setting_name, config_file):
|
||||
"""Return the value of a setting from a config file."""
|
||||
with open(config_file, 'r') as config:
|
||||
for line in config:
|
||||
if line.startswith(setting_name):
|
||||
return re.findall(r'["\'][^"\']*["\']', line)[0].strip('"\'')
|
||||
|
||||
return None
|
||||
|
||||
user_config = '/etc/mediawiki/FreedomBoxSettings.php'
|
||||
static_config = '/etc/mediawiki/FreedomBoxStaticSettings.php'
|
||||
|
||||
return _find_skin(user_config) or _find_skin(static_config)
|
||||
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)
|
||||
|
||||
|
||||
def get_default_skin():
|
||||
"""Return the value of the default skin."""
|
||||
return _get_config_value('$wgDefaultSkin')
|
||||
|
||||
|
||||
def set_default_skin(skin):
|
||||
"""Set the value of the default skin."""
|
||||
actions.superuser_run('mediawiki', ['set-default-skin', skin])
|
||||
|
||||
|
||||
def get_server_url():
|
||||
"""Return the value of the server URL."""
|
||||
server_url = _get_config_value('$wgServer')
|
||||
return urlparse(server_url).netloc
|
||||
|
||||
|
||||
def set_server_url(server_url):
|
||||
"""Set the value of $wgServer."""
|
||||
actions.superuser_run('mediawiki',
|
||||
['set-server-url', f'https://{server_url}'])
|
||||
|
||||
@ -38,3 +38,6 @@ $wgSessionCacheType = CACHE_DB;
|
||||
|
||||
# Use the mobile-friendly skin Timeless by default
|
||||
$wgDefaultSkin = "timeless";
|
||||
|
||||
# Domain Name
|
||||
$wgServer = "https://freedombox.local";
|
||||
|
||||
@ -6,6 +6,8 @@ FreedomBox app for configuring MediaWiki.
|
||||
import pathlib
|
||||
|
||||
from django import forms
|
||||
from django.forms import Widget
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
@ -19,6 +21,29 @@ def get_skins():
|
||||
if skin.is_dir()]
|
||||
|
||||
|
||||
class PrependWidget(Widget):
|
||||
"""Widget to create input-groups with prepended text."""
|
||||
|
||||
def __init__(self, base_widget, data, *args, **kwargs):
|
||||
"""Initialize widget and get base instance"""
|
||||
super(PrependWidget, self).__init__(*args, **kwargs)
|
||||
self.base_widget = base_widget(*args, **kwargs)
|
||||
self.data = data
|
||||
|
||||
def render(self, name, value, attrs=None, renderer=None):
|
||||
"""Render base widget and add bootstrap spans."""
|
||||
attrs['class'] = 'form-control'
|
||||
field = self.base_widget.render(name, value, attrs, renderer)
|
||||
widget_html = '''
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">
|
||||
%(data)s
|
||||
</span>
|
||||
%(field)s
|
||||
</div>'''
|
||||
return mark_safe((widget_html) % {'field': field, 'data': self.data})
|
||||
|
||||
|
||||
class MediaWikiForm(forms.Form): # pylint: disable=W0232
|
||||
"""MediaWiki configuration form."""
|
||||
password = forms.CharField(
|
||||
@ -27,6 +52,12 @@ class MediaWikiForm(forms.Form): # pylint: disable=W0232
|
||||
'(admin). Leave this field blank to keep the current password.'),
|
||||
required=False, widget=forms.PasswordInput)
|
||||
|
||||
server_url = forms.CharField(
|
||||
label=_('Server URL'), required=False, help_text=_(
|
||||
'Used by MediaWiki to generate URLs that point to the wiki '
|
||||
'such as in footer, feeds and emails.'),
|
||||
widget=PrependWidget(base_widget=forms.TextInput, data='https://'))
|
||||
|
||||
enable_public_registrations = forms.BooleanField(
|
||||
label=_('Enable public registrations'), required=False,
|
||||
help_text=_('If enabled, anyone on the internet will be able to '
|
||||
|
||||
53
plinth/modules/mediawiki/tests/conftest.py
Normal file
53
plinth/modules/mediawiki/tests/conftest.py
Normal file
@ -0,0 +1,53 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""
|
||||
Common test fixtures for MediaWiki.
|
||||
"""
|
||||
|
||||
import shutil
|
||||
import importlib
|
||||
import pathlib
|
||||
import types
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
current_directory = pathlib.Path(__file__).parent
|
||||
|
||||
|
||||
def _load_actions_module():
|
||||
actions_file_path = str(current_directory / '..' / '..' / '..' / '..' /
|
||||
'actions' / 'mediawiki')
|
||||
loader = importlib.machinery.SourceFileLoader('mediawiki',
|
||||
actions_file_path)
|
||||
module = types.ModuleType(loader.name)
|
||||
loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
actions = _load_actions_module()
|
||||
|
||||
|
||||
@pytest.fixture(name='call_action')
|
||||
def fixture_call_action(capsys, conf_file):
|
||||
"""Run actions with custom root path."""
|
||||
|
||||
def _call_action(module_name, args, **kwargs):
|
||||
actions.CONF_FILE = conf_file
|
||||
with patch('argparse._sys.argv', [module_name] + args):
|
||||
actions.main()
|
||||
captured = capsys.readouterr()
|
||||
return captured.out
|
||||
|
||||
return _call_action
|
||||
|
||||
|
||||
@pytest.fixture(name='conf_file')
|
||||
def fixture_conf_file(tmp_path):
|
||||
"""Uses a dummy configuration file."""
|
||||
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)
|
||||
@ -7,6 +7,7 @@ Feature: MediaWiki Wiki Engine
|
||||
Background:
|
||||
Given I'm a logged in user
|
||||
Given the mediawiki application is installed
|
||||
Given the server url is set to test config url
|
||||
|
||||
Scenario: Enable mediawiki application
|
||||
Given the mediawiki application is disabled
|
||||
|
||||
@ -4,14 +4,21 @@ Functional, browser based tests for mediawiki app.
|
||||
"""
|
||||
|
||||
import pathlib
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from pytest_bdd import parsers, scenarios, then, when
|
||||
from pytest_bdd import given, parsers, scenarios, then, when
|
||||
|
||||
from plinth.tests import functional
|
||||
from plinth.tests.functional import config
|
||||
|
||||
scenarios('mediawiki.feature')
|
||||
|
||||
|
||||
@given(parsers.parse('the server url is set to test config url'))
|
||||
def set_server_url(session_browser):
|
||||
_set_server_url(session_browser)
|
||||
|
||||
|
||||
@when(parsers.parse('I enable mediawiki public registrations'))
|
||||
def enable_mediawiki_public_registrations(session_browser):
|
||||
_enable_public_registrations(session_browser)
|
||||
@ -57,8 +64,7 @@ def mediawiki_allows_anonymous_reads_edits(session_browser):
|
||||
@then(
|
||||
parsers.parse(
|
||||
'the mediawiki site should not allow anonymous reads and writes'))
|
||||
def mediawiki_does_not_allow__account_creation_anonymous_reads_edits(
|
||||
session_browser):
|
||||
def mediawiki_does_not_allow_anonymous_reads_edits(session_browser):
|
||||
_verify_no_anonymous_reads_edits_link(session_browser)
|
||||
|
||||
|
||||
@ -216,3 +222,11 @@ def __has_main_page(browser):
|
||||
functional.visit(browser, '/mediawiki/Main_Page')
|
||||
content = browser.find_by_id('mw-content-text').first
|
||||
return 'This page has been deleted.' not in content.text
|
||||
|
||||
|
||||
def _set_server_url(browser):
|
||||
"""Set the value of server url to the value in the given env_var."""
|
||||
functional.nav_to_module(browser, 'mediawiki')
|
||||
server_url = urlparse(config['DEFAULT']['url']).netloc
|
||||
browser.find_by_id('id_server_url').fill(server_url)
|
||||
functional.submit(browser, form_class='form-configuration')
|
||||
|
||||
46
plinth/modules/mediawiki/tests/test_settings.py
Normal file
46
plinth/modules/mediawiki/tests/test_settings.py
Normal file
@ -0,0 +1,46 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""
|
||||
Test module for MediaWiki utility functions.
|
||||
"""
|
||||
|
||||
import pathlib
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from plinth.modules import mediawiki
|
||||
|
||||
|
||||
@pytest.fixture(name='test_configuration', autouse=True)
|
||||
def fixture_test_configuration(call_action, conf_file):
|
||||
"""Use a separate MediaWiki configuration for tests.
|
||||
|
||||
Uses local FreedomBoxStaticSettings.php, a temp version of
|
||||
FreedomBoxSettings.php and patches actions.superuser_run with the fixture
|
||||
call_action
|
||||
|
||||
"""
|
||||
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), \
|
||||
patch('plinth.actions.superuser_run', call_action):
|
||||
yield
|
||||
|
||||
|
||||
def test_default_skin():
|
||||
"""Test getting and setting the default skin."""
|
||||
assert mediawiki.get_default_skin() == 'timeless'
|
||||
new_skin = 'vector'
|
||||
mediawiki.set_default_skin(new_skin)
|
||||
assert mediawiki.get_default_skin() == new_skin
|
||||
|
||||
|
||||
def test_server_url():
|
||||
"""Test getting and setting $wgServer."""
|
||||
assert mediawiki.get_server_url() == 'freedombox.local'
|
||||
new_server_url = 'mydomain.freedombox.rocks'
|
||||
mediawiki.set_server_url(new_server_url)
|
||||
assert mediawiki.get_server_url() == new_server_url
|
||||
@ -11,7 +11,7 @@ from django.utils.translation import ugettext as _
|
||||
from plinth import actions, views
|
||||
from plinth.modules import mediawiki
|
||||
|
||||
from . import (get_default_skin, is_private_mode_enabled,
|
||||
from . import (get_default_skin, get_server_url, is_private_mode_enabled,
|
||||
is_public_registration_enabled)
|
||||
from .forms import MediaWikiForm
|
||||
|
||||
@ -30,7 +30,8 @@ class MediaWikiAppView(views.AppView):
|
||||
initial.update({
|
||||
'enable_public_registrations': is_public_registration_enabled(),
|
||||
'enable_private_mode': is_private_mode_enabled(),
|
||||
'default_skin': get_default_skin()
|
||||
'default_skin': get_default_skin(),
|
||||
'server_url': get_server_url()
|
||||
})
|
||||
return initial
|
||||
|
||||
@ -39,15 +40,15 @@ class MediaWikiAppView(views.AppView):
|
||||
old_config = self.get_initial()
|
||||
new_config = form.cleaned_data
|
||||
|
||||
def is_unchanged(key):
|
||||
return old_config[key] == new_config[key]
|
||||
def is_changed(key):
|
||||
return old_config.get(key) != new_config.get(key)
|
||||
|
||||
if new_config['password']:
|
||||
actions.superuser_run('mediawiki', ['change-password'],
|
||||
input=new_config['password'].encode())
|
||||
messages.success(self.request, _('Password updated'))
|
||||
|
||||
if not is_unchanged('enable_public_registrations'):
|
||||
if is_changed('enable_public_registrations'):
|
||||
# note action public-registration restarts, if running now
|
||||
if new_config['enable_public_registrations']:
|
||||
if not new_config['enable_private_mode']:
|
||||
@ -65,7 +66,7 @@ class MediaWikiAppView(views.AppView):
|
||||
messages.success(self.request,
|
||||
_('Public registrations disabled'))
|
||||
|
||||
if not is_unchanged('enable_private_mode'):
|
||||
if is_changed('enable_private_mode'):
|
||||
if new_config['enable_private_mode']:
|
||||
actions.superuser_run('mediawiki', ['private-mode', 'enable'])
|
||||
messages.success(self.request, _('Private mode enabled'))
|
||||
@ -80,9 +81,12 @@ class MediaWikiAppView(views.AppView):
|
||||
shortcut = mediawiki.app.get_component('shortcut-mediawiki')
|
||||
shortcut.login_required = new_config['enable_private_mode']
|
||||
|
||||
if not is_unchanged('default_skin'):
|
||||
actions.superuser_run(
|
||||
'mediawiki', ['set-default-skin', new_config['default_skin']])
|
||||
if is_changed('default_skin'):
|
||||
mediawiki.set_default_skin(new_config['default_skin'])
|
||||
messages.success(self.request, _('Default skin changed'))
|
||||
|
||||
if is_changed('server_url'):
|
||||
mediawiki.set_server_url(new_config['server_url'])
|
||||
messages.success(self.request, _('Server URL updated'))
|
||||
|
||||
return super().form_valid(form)
|
||||
|
||||
@ -152,7 +152,10 @@ def is_available(browser, site_name):
|
||||
not_404 = '404' not in browser.title
|
||||
# The site might have a default path after the sitename,
|
||||
# e.g /mediawiki/Main_Page
|
||||
no_redirect = browser.url.startswith(url_to_visit.strip('/'))
|
||||
print('URL =', browser.url, url_to_visit, browser.title)
|
||||
browser_url = browser.url.partition('://')[2]
|
||||
url_to_visit_without_proto = url_to_visit.strip('/').partition('://')[2]
|
||||
no_redirect = browser_url.startswith(url_to_visit_without_proto)
|
||||
return not_404 and no_redirect
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user