FreedomBox/plinth/forms.py
Alice Kile 0b5b384651
app: Separate app enable/disable form from config form
- Introduce new API to mark an app that it can't be disabled.

- Mark jsxc, storage, config, upgrade and firewall apps as can't be disabled.

- Fixed functional tests

- Replaced AppForm with forms.Form in all modules' forms.py.

- Remove app.template.js.

- Remove unused styles.

- Remove app status checks in form_valid of Deluge, Diaspora, Matrix, Ejabberd,
MediaWiki, Storage, Transmission, Quassel

- Purge unused is_enabled context variables (Ikiwiki)

- ejabberd: Minor cleanup in template

- jsxc: Cleanup unneeded overrides

- tahoe: Cleanup unnecessary overrides

Tests performed:

- For all apps affected, test enable/disable button works and submitting
configuration form works: with changes updates message and without changes
'settings unchanged' message.
  - avahi
  - bind
  - cockpit
  - SKIP: coquelicot
  - datetime
  - deluge
  - SKIP: diaspora
  - ejabberd
  - gitweb
  - i2p
  - infinoted
  - ikiwiki
  - matrixsynapse
  - mediawiki
  - minetest
  - minidlna
  - mldonkey
  - mumble
  - pagekite
  - privoxy
  - quassel
  - radicale
  - roundcube
  - SKIP: samba
  - searx
  - SKIP: shaarli
  - shadowsocks
  - ssh
  - tahoe
  - transmission
  - FAIL: tt-rss (not installable)
  - wireguard
- Deluge test that configuration changes when app is disabled work
- Quassel test that setting the domain works when app is diabled
- Transmission test that setting the domain works when app is diabled
- Ikiwiki create form works properly
- Enable/disable button appears as expected when enabled and when disabled
- Enable/disable button works without Javascript
- Functional tests work for affected apps, Tor and OpenVPN
- AppForm is removed from developer documentation
  - Forms reference
  - Customizing tutorial
- Test all apps using directory select form
  - Transmission
  - Deluge
- Visit each template that overrides block configuration and ensure that it is
loaded properly and the display is as expected.
- All apps that use AppView that are not tested above should not have an
enable/disable button. That is JSXC, update, config, firewall, storage, users.

Signed-off-by: Alice Kile <buoyantair@protonmail.com>
Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: Veiko Aasa <veiko17@disroot.org>
2020-03-29 09:42:31 +03:00

117 lines
4.4 KiB
Python

# SPDX-License-Identifier: AGPL-3.0-or-later
"""
Common forms for use by modules.
"""
import os
from itertools import chain
from django import forms
from django.conf import settings
from django.forms import CheckboxInput
from django.utils import translation
from django.utils.safestring import mark_safe
from django.utils.translation import get_language_info
from django.utils.translation import ugettext_lazy as _
import plinth
class AppEnableDisableForm(forms.Form):
"""Form to enable / disable an app."""
should_enable = forms.BooleanField(widget=forms.HiddenInput,
required=False)
class DomainSelectionForm(forms.Form):
"""Form for selecting a domain name to be used for
distributed federated applications
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
from plinth.modules.names.components import DomainName
domains = list(DomainName.list_names())
self.fields['domain_name'].choices = zip(domains, domains)
domain_name = forms.ChoiceField(
label=_('Select a domain name to be used with this application'),
help_text=_(
'Warning! The application may not work properly if domain name is '
'changed later.'), choices=[])
class LanguageSelectionFormMixin:
"""Form mixin for selecting the user's preferred language."""
language = forms.ChoiceField(
label=_('Language'),
help_text=_('Language to use for presenting this web interface'),
required=False)
def __init__(self, *args, **kwargs):
"""Initialize the form to fill language choice values."""
super().__init__(*args, **kwargs)
supported_languages = [
(None, _('Use the language preference set in the browser'))
]
def _get_local_name(language_code, language_name):
try:
return get_language_info(language_code)['name_local']
except KeyError:
return language_name
for language_code, language_name in settings.LANGUAGES:
locale_code = translation.to_locale(language_code)
plinth_dir = os.path.dirname(plinth.__file__)
if language_code == 'en' or os.path.exists(
os.path.join(plinth_dir, 'locale', locale_code)):
supported_languages.append(
(language_code,
_get_local_name(language_code, language_name)))
self.fields['language'].choices = supported_languages
class LanguageSelectionForm(LanguageSelectionFormMixin, forms.Form):
"""Language selection form."""
language = LanguageSelectionFormMixin.language
class CheckboxSelectMultipleWithReadOnly(forms.widgets.CheckboxSelectMultiple):
"""
Subclass of Django's CheckboxSelectMultiple widget that allows setting
individual fields as readonly
To mark a feature as readonly an option, pass a dict instead of a string
for its label, of the form: {'label': 'option label', 'disabled': True}
Derived from https://djangosnippets.org/snippets/2786/
"""
def render(self, name, value, attrs=None, choices=(), renderer=None):
if value is None:
value = []
final_attrs = self.build_attrs(attrs)
output = [u'<ul>']
global_readonly = 'readonly' in final_attrs
str_values = set([v for v in value])
for i, (option_value,
option_label) in enumerate(chain(self.choices, choices)):
if not global_readonly and 'readonly' in final_attrs:
# If the entire group is readonly keep all options readonly
del final_attrs['readonly']
if isinstance(option_label, dict):
if dict.get(option_label, 'readonly'):
final_attrs = dict(final_attrs, readonly='readonly')
option_label = option_label['label']
final_attrs = dict(final_attrs, id='{}_{}'.format(attrs['id'], i))
label_for = u' for="{}"'.format(final_attrs['id'])
cb = CheckboxInput(final_attrs,
check_test=lambda value: value in str_values)
rendered_cb = cb.render(name, option_value)
output.append(u'<li><label%s>%s %s</label></li>' %
(label_for, rendered_cb, option_label))
output.append(u'</ul>')
return mark_safe(u'\n'.join(output))