Turn frontpage shortcut into an app component

- Turn frontpage shortcut into an App component. Add tests and full
  documentation.

- Overridden implementations for tahoe, diaspora, mediawiki shortcuts to handle
  special cases. Special handling for ikiwiki.

- Extend App API for removing and retrieving a component.

- Add clients information into shortcuts to avoid hacks when presenting
  shortcuts to Mobile devices via API.

- Fixed unnecessary stripping and adding of '/' when setting home page redirect
  URLs. This fixes problem with setting Cockpit as home page.

- Replaced the use of term 'app' in favor of 'shortcut' as the term when setting
  frontpage shortcuts as home page.

- JSXC shortcut does not require login.

- Don't show shadowsocks for anonymous users.

- Simplify showing selected shortcut details.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
This commit is contained in:
Sunil Mohan Adapa 2019-05-17 15:39:39 -07:00
parent b96d901071
commit 75c57d3e00
No known key found for this signature in database
GPG Key ID: 43EA1CFF0AA7C5F2
41 changed files with 601 additions and 479 deletions

View File

@ -53,7 +53,7 @@ def subcommand_set_home_page(arguments):
conf_file_path = os.path.join('/etc/apache2/conf-available', conf_file_path = os.path.join('/etc/apache2/conf-available',
APACHE_HOMEPAGE_CONF_FILE_NAME) APACHE_HOMEPAGE_CONF_FILE_NAME)
redirect_rule = 'RedirectMatch "^/$" "/{}"\n'.format(arguments.homepage) redirect_rule = 'RedirectMatch "^/$" "{}"\n'.format(arguments.homepage)
with open(conf_file_path, 'w') as conf_file: with open(conf_file_path, 'w') as conf_file:
conf_file.write(redirect_rule) conf_file.write(redirect_rule)

View File

@ -40,6 +40,16 @@ class App:
self.components[component.component_id] = component self.components[component.component_id] = component
return self return self
def remove(self, component_id):
"""Remove a component from the app."""
component = self.components[component_id]
del self.components[component_id]
return component
def get(self, component_id):
"""Return a component given the component's ID."""
return self.components[component_id]
def enable(self): def enable(self):
"""Enable all the components of the app.""" """Enable all the components of the app."""
for component in self.components.values(): for component in self.components.values():

View File

@ -20,61 +20,149 @@ Manage application shortcuts on front page.
import json import json
import os import os
from plinth import cfg from plinth import app, cfg
from . import actions from . import actions
shortcuts = {}
class Shortcut(app.FollowerComponent):
"""An application component for handling shortcuts."""
def get_shortcuts(username=None, web_apps_only=False, sort_by='label'): _all_shortcuts = {}
"""Return menu items in sorted order according to current locale."""
shortcuts_to_return = {} def __init__(self, component_id, name, short_description=None, icon=None,
if username: url=None, description=None, configure_url=None, clients=None,
login_required=False, allowed_groups=None):
"""Initialize the frontpage shortcut component for an app.
When a user visits this web interface, they are first shown the
frontpage. It's primary contents are the list of shortcuts to services
that user may use on the server. If a service requires logging in, it
does not show up to anonymous users. If a service requires a user to be
part of a group, that service is only shown to those users.
'component_id' must be a unique string across all apps and components
of a app. Conventionally starts with 'shortcut-'.
'name' is the mandatory title for the shortcut.
'short_description' is an optional secondary title for the shortcut.
'icon' is used to find a suitable image to represent the shortcut.
'url' is link to which the user is redirected when the shortcut is
activated. This is typically the web interface for a particular service
provided by the app. For shortcuts that should simply additional
information this value must be None.
'details' are additional information that the user is shown when the
shortcut is activated. This must be provided instead of 'url' and must
be 'None' if 'url' is provided.
'configure_url' is the page to which the user may be redirected if they
wish to change the settings for the app or one of its services. This is
only used when 'url' is 'None'. It is optionally provided along with
'details'.
'clients' is a list of clients software that can used to access the
service offered by the shortcut. This should be a valid client
information structure as validated by clients.py:validate().
If 'login_required' is true, only logged-in users will be shown this
shortcut. Anonymous users visiting the frontpage won't be shown this
shortcut.
'allowed_groups' specifies a list of user groups to whom this shortcut
must be shown. All other user groups will not be shown this shortcut on
the frontpage.
"""
super().__init__(component_id)
if not url:
url = '?selected={id}'.format(id=component_id)
self.name = name
self.short_description = short_description
self.url = url
self.icon = icon
self.description = description
self.configure_url = configure_url
self.clients = clients
self.login_required = login_required
self.allowed_groups = set(allowed_groups) if allowed_groups else None
self._all_shortcuts[self.component_id] = self
def remove(self):
"""Remove this shortcut from global list of shortcuts."""
del self._all_shortcuts[self.component_id]
@classmethod
def list(cls, username=None, web_apps_only=False, sort_by='name'):
"""Return menu items in sorted order according to current locale."""
shortcuts_to_return = cls._list_for_user(username)
if web_apps_only:
shortcuts_to_return = {
_id: shortcut
for _id, shortcut in shortcuts_to_return.items()
if not shortcut.url.startswith('?selected=')
}
return sorted(shortcuts_to_return.values(),
key=lambda item: getattr(item, sort_by).lower())
@classmethod
def _list_for_user(cls, username=None):
"""Return menu items for a particular user or anonymous user."""
if not username:
return cls._all_shortcuts
# XXX: Turn this into an API call in users module and cache
output = actions.superuser_run('users', ['get-user-groups', username]) output = actions.superuser_run('users', ['get-user-groups', username])
user_groups = set(output.strip().split('\n')) user_groups = set(output.strip().split('\n'))
if 'admin' in user_groups: # Admin has access to all services if 'admin' in user_groups: # Admin has access to all services
shortcuts_to_return = shortcuts return cls._all_shortcuts
else:
for shortcut_id, shortcut in shortcuts.items():
if shortcut['allowed_groups']:
if not user_groups.isdisjoint(shortcut['allowed_groups']):
shortcuts_to_return[shortcut_id] = shortcut
else:
shortcuts_to_return[shortcut_id] = shortcut
else:
shortcuts_to_return = shortcuts
if web_apps_only: shortcuts = {}
shortcuts_to_return = { for shortcut_id, shortcut in cls._all_shortcuts.items():
_id: shortcut if shortcut.allowed_groups and \
for _id, shortcut in shortcuts_to_return.items() user_groups.isdisjoint(shortcut.allowed_groups):
if not shortcut['url'].startswith('?selected=') continue
}
return sorted(shortcuts_to_return.values(), key=lambda item: item[sort_by]) shortcuts[shortcut_id] = shortcut
return shortcuts
def add_custom_shortcuts(): def add_custom_shortcuts():
custom_shortcuts = get_custom_shortcuts() custom_shortcuts = get_custom_shortcuts()
if not custom_shortcuts:
return
if custom_shortcuts: for shortcut in custom_shortcuts['shortcuts']:
for shortcut in custom_shortcuts['shortcuts']: web_app_url = _extract_web_app_url(shortcut)
web_app_url = _extract_web_app_url(shortcut) if not web_app_url:
if web_app_url: continue
add_shortcut(None, shortcut['name'],
shortcut['short_description'], Shortcut(None, shortcut['name'], shortcut['short_description'],
icon=shortcut['icon_url'], url=web_app_url) icon=shortcut['icon_url'], url=web_app_url)
def _extract_web_app_url(custom_shortcut): def _extract_web_app_url(custom_shortcut):
if custom_shortcut.get('clients'): if not custom_shortcut.get('clients'):
for client in custom_shortcut['clients']: return None
if client.get('platforms'):
for platform in client['platforms']: for client in custom_shortcut['clients']:
if platform['type'] == 'web': if not client.get('platforms'):
return platform['url'] continue
for platform in client['platforms']:
if platform['type'] == 'web':
return platform['url']
return None
def get_custom_shortcuts(): def get_custom_shortcuts():
@ -85,58 +173,3 @@ def get_custom_shortcuts():
custom_shortcuts = json.load(shortcuts) custom_shortcuts = json.load(shortcuts)
return custom_shortcuts return custom_shortcuts
return None return None
def add_shortcut(shortcut_id, name, short_description="", login_required=False,
icon=None, url=None, details=None, configure_url=None,
allowed_groups=None):
"""Add shortcut to front page."""
if not url:
url = '?selected={id}'.format(id=shortcut_id)
if not icon:
icon = shortcut_id
label = '{0}\n({1})'.format(short_description, name) if short_description \
else name
shortcuts[shortcut_id] = {
'id': shortcut_id,
'name': name,
'short_description': short_description,
'label': label,
'url': url,
'icon': icon,
'login_required': login_required,
'details': details,
'configure_url': configure_url,
'hidden': False,
'allowed_groups': allowed_groups
}
def remove_shortcut(shortcut_id):
"""
Remove shortcut from front page.
If shortcut_id ends with *, remove all shortcuts with that prefix.
"""
def match(item):
if shortcut_id[-1] == '*':
return item['id'].startswith(shortcut_id[:-1])
return item['id'] == shortcut_id
global shortcuts
shortcuts = {
shortcut_id: shortcut
for shortcut_id, shortcut in shortcuts.items() if not match(shortcut)
}
def hide_shortcut(shortcut_id, hide=True):
"""Mark a shortcut as hidden or not hidden."""
global shortcuts
shortcuts[shortcut_id]['hidden'] = hide

View File

@ -25,7 +25,7 @@ from django.core.serializers.json import DjangoJSONEncoder
from django.http import HttpResponse from django.http import HttpResponse
from django.templatetags.static import static from django.templatetags.static import static
from plinth import frontpage, module_loader from plinth import frontpage
from plinth.modules import names from plinth.modules import names
@ -52,8 +52,9 @@ def shortcuts(request, **kwargs):
def get_shortcuts_as_json(username=None): def get_shortcuts_as_json(username=None):
shortcuts = [ shortcuts = [
_get_shortcut_data(shortcut['id'].split('_')[0], shortcut) _get_shortcut_data(shortcut)
for shortcut in frontpage.get_shortcuts(username) if shortcut['id'] for shortcut in frontpage.Shortcut.list(username)
if shortcut.component_id
] ]
custom_shortcuts = frontpage.get_custom_shortcuts() custom_shortcuts = frontpage.get_custom_shortcuts()
if custom_shortcuts: if custom_shortcuts:
@ -61,17 +62,17 @@ def get_shortcuts_as_json(username=None):
return {'shortcuts': shortcuts} return {'shortcuts': shortcuts}
def _get_shortcut_data(module_name, shortcut): def _get_shortcut_data(shortcut):
"""Return detailed information about a shortcut.""" """Return detailed information about a shortcut."""
module = module_loader.loaded_modules[module_name]
shortcut_data = { shortcut_data = {
'name': shortcut['name'], 'name': shortcut.name,
'short_description': shortcut['short_description'], 'short_description': shortcut.short_description,
'description': shortcut['details'], 'description': shortcut.description,
'icon_url': _get_icon_url(shortcut['icon']), 'icon_url': _get_icon_url(shortcut.icon),
'clients': copy.deepcopy(getattr(module, 'clients', None)) 'clients': copy.deepcopy(shortcut.clients),
} }
if module_name == 'ikiwiki': # XXX: Fix the hardcoding
if shortcut.name.startswith('shortcut-ikiwiki-'):
shortcut_data['clients'][0]['platforms'][0]['url'] += '/{}'.format( shortcut_data['clients'][0]['platforms'][0]['url'] += '/{}'.format(
shortcut['name']) shortcut['name'])
return shortcut_data return shortcut_data

View File

@ -74,6 +74,12 @@ class CockpitApp(app_module.App):
parent_url_name='system') parent_url_name='system')
self.add(menu_item) self.add(menu_item)
shortcut = frontpage.Shortcut('shortcut-cockpit', name,
short_description=short_description,
icon='cockpit', url='/_cockpit/',
clients=clients, login_required=True)
self.add(shortcut)
def init(): def init():
"""Intialize the module.""" """Intialize the module."""
@ -89,7 +95,6 @@ def init():
disable=disable) disable=disable)
if is_enabled(): if is_enabled():
add_shortcut()
app.set_enabled(True) app.set_enabled(True)
domain_added.connect(on_domain_added) domain_added.connect(on_domain_added)
@ -112,14 +117,7 @@ def setup(helper, old_version=None):
], is_external=True, is_enabled=is_enabled, enable=enable, ], is_external=True, is_enabled=is_enabled, enable=enable,
disable=disable) disable=disable)
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut) helper.call('post', app.enable)
def add_shortcut():
"""Add a shortcut the frontpage."""
frontpage.add_shortcut('cockpit', name,
short_description=short_description,
url='/_cockpit/', login_required=True)
def is_enabled(): def is_enabled():
@ -131,14 +129,12 @@ def is_enabled():
def enable(): def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('cockpit', ['enable']) actions.superuser_run('cockpit', ['enable'])
add_shortcut()
app.enable() app.enable()
def disable(): def disable():
"""Disable the module.""" """Disable the module."""
actions.superuser_run('cockpit', ['disable']) actions.superuser_run('cockpit', ['disable'])
frontpage.remove_shortcut('cockpit')
app.disable() app.disable()

View File

@ -26,7 +26,7 @@ from django.utils.translation import ugettext_lazy
from plinth import actions from plinth import actions
from plinth import app as app_module from plinth import app as app_module
from plinth import menu from plinth import frontpage, menu
from plinth.modules import firewall from plinth.modules import firewall
from plinth.modules.names import SERVICES from plinth.modules.names import SERVICES
from plinth.signals import domain_added from plinth.signals import domain_added
@ -72,7 +72,7 @@ def get_hostname():
return socket.gethostname() return socket.gethostname()
def get_home_page(): def _get_home_page_url():
"""Get the default application for the domain.""" """Get the default application for the domain."""
aug = augeas.Augeas( aug = augeas.Augeas(
flags=augeas.Augeas.NO_LOAD + augeas.Augeas.NO_MODL_AUTOLOAD) flags=augeas.Augeas.NO_LOAD + augeas.Augeas.NO_MODL_AUTOLOAD)
@ -84,22 +84,46 @@ def get_home_page():
aug.defvar('conf', '/files' + conf_file) aug.defvar('conf', '/files' + conf_file)
app_path = 'plinth'
for match in aug.match('/files' + conf_file + for match in aug.match('/files' + conf_file +
'/directive["RedirectMatch"]'): '/directive["RedirectMatch"]'):
if aug.get(match + "/arg[1]") == '''"^/$"''': if aug.get(match + "/arg[1]") == '''"^/$"''':
app_path = aug.get(match + "/arg[2]") return aug.get(match + "/arg[2]").strip('"')
# match this against the app_id in the entries of frontpage.get_shortcuts() return None
# The underscore is to handle Ikiwiki app_ids
app = app_path.strip('/"').replace('/', '_')
if app == 'index.html': def get_home_page():
"""Return the shortcut ID that is set as current home page."""
url = _get_home_page_url()
if url in ['/plinth/', '/plinth', 'plinth']:
return 'plinth'
if url == '/index.html':
return 'apache-default' return 'apache-default'
elif app.endswith('jsxc'):
return 'jsxc' shortcuts = frontpage.Shortcut.list()
for shortcut in shortcuts:
if shortcut.url == url:
return shortcut.component_id
return None
def change_home_page(shortcut_id):
"""Change the FreedomBox's default redirect to URL of the shortcut specified."""
if shortcut_id == 'plinth':
url = '/plinth/'
elif shortcut_id == 'apache-default':
url = '/index.html'
else: else:
return app shortcuts = frontpage.Shortcut.list()
url = [
shortcut.url for shortcut in shortcuts
if shortcut.component_id == shortcut_id
][0]
# URL may be a reverse_lazy() proxy
actions.superuser_run('config', ['set-home-page', str(url)])
def init(): def init():

View File

@ -43,12 +43,13 @@ def domain_label_validator(domainname):
def get_homepage_choices(): def get_homepage_choices():
shortcuts = frontpage.get_shortcuts(web_apps_only=True, sort_by='name') """Return list of drop down choices for home page."""
apps = [(shortcut['id'], shortcut['name']) for shortcut in shortcuts shortcuts = frontpage.Shortcut.list(web_apps_only=True)
if shortcut['id']] shortcut_choices = [(shortcut.component_id, shortcut.name)
for shortcut in shortcuts if shortcut.is_enabled()]
apache_default = ('apache-default', _('Apache Default')) apache_default = ('apache-default', _('Apache Default'))
plinth = ('plinth', _('FreedomBox Service (Plinth)')) plinth = ('plinth', _('FreedomBox Service (Plinth)'))
return [apache_default, plinth] + apps return [apache_default, plinth] + shortcut_choices
class ConfigurationForm(forms.Form): class ConfigurationForm(forms.Form):

View File

@ -24,7 +24,7 @@ from django.contrib import messages
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from plinth import actions, frontpage from plinth import actions
from plinth.modules import config, firewall from plinth.modules import config, firewall
from plinth.modules.names import SERVICES from plinth.modules.names import SERVICES
from plinth.signals import (domain_added, domain_removed, domainname_change, from plinth.signals import (domain_added, domain_removed, domainname_change,
@ -75,8 +75,8 @@ def _apply_changes(request, old_status, new_status):
except Exception as exception: except Exception as exception:
messages.error( messages.error(
request, request,
_('Error setting hostname: {exception}') _('Error setting hostname: {exception}').format(
.format(exception=exception)) exception=exception))
else: else:
messages.success(request, _('Hostname set')) messages.success(request, _('Hostname set'))
@ -86,39 +86,23 @@ def _apply_changes(request, old_status, new_status):
except Exception as exception: except Exception as exception:
messages.error( messages.error(
request, request,
_('Error setting domain name: {exception}') _('Error setting domain name: {exception}').format(
.format(exception=exception)) exception=exception))
else: else:
messages.success(request, _('Domain name set')) messages.success(request, _('Domain name set'))
if old_status['homepage'] != new_status['homepage']: if old_status['homepage'] != new_status['homepage']:
try: try:
change_home_page(new_status['homepage']) config.change_home_page(new_status['homepage'])
except Exception as exception: except Exception as exception:
messages.error( messages.error(
request, request,
_('Error setting webserver home page: {exception}') _('Error setting webserver home page: {exception}').format(
.format(exception=exception)) exception=exception))
else: else:
messages.success(request, _('Webserver home page set')) messages.success(request, _('Webserver home page set'))
def change_home_page(app_id):
"""Changes the FreedomBox's default app to the app specified by app_id."""
if app_id == 'plinth':
url = '/plinth'
elif app_id == 'apache-default':
url = '/index.html'
else:
shortcuts = frontpage.get_shortcuts()
url = [
shortcut['url'] for shortcut in shortcuts
if shortcut['id'] == app_id
][0]
actions.superuser_run('config', ['set-home-page', url.strip('/')])
def set_hostname(hostname): def set_hostname(hostname):
"""Sets machine hostname to hostname""" """Sets machine hostname to hostname"""
old_hostname = config.get_hostname() old_hostname = config.get_hostname()

View File

@ -67,6 +67,12 @@ class CoquelicotApp(app_module.App):
parent_url_name='apps') parent_url_name='apps')
self.add(menu_item) self.add(menu_item)
shortcut = frontpage.Shortcut('shortcut-coquelicot', name,
short_description=short_description,
icon='coquelicot', url='/coquelicot',
clients=clients, login_required=True)
self.add(shortcut)
def init(): def init():
"""Intialize the module.""" """Intialize the module."""
@ -83,7 +89,6 @@ def init():
is_running=is_running) is_running=is_running)
if is_enabled(): if is_enabled():
add_shortcut()
app.set_enabled(True) app.set_enabled(True)
@ -100,17 +105,9 @@ def setup(helper, old_version=None):
disable=disable, disable=disable,
is_running=is_running) is_running=is_running)
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
helper.call('post', app.enable) helper.call('post', app.enable)
def add_shortcut():
"""Helper method to add a shortcut to the frontpage."""
frontpage.add_shortcut('coquelicot', name,
short_description=short_description,
url='/coquelicot', login_required=True)
def is_running(): def is_running():
"""Return whether the service is running.""" """Return whether the service is running."""
return action_utils.service_is_running('coquelicot') return action_utils.service_is_running('coquelicot')
@ -125,14 +122,12 @@ def is_enabled():
def enable(): def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('coquelicot', ['enable']) actions.superuser_run('coquelicot', ['enable'])
add_shortcut()
app.enable() app.enable()
def disable(): def disable():
"""Disable the module.""" """Disable the module."""
actions.superuser_run('coquelicot', ['disable']) actions.superuser_run('coquelicot', ['disable'])
frontpage.remove_shortcut('coquelicot')
app.disable() app.disable()

View File

@ -69,6 +69,12 @@ class DelugeApp(app_module.App):
'deluge:index', parent_url_name='apps') 'deluge:index', parent_url_name='apps')
self.add(menu_item) self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-deluge', name, short_description=short_description,
url='/deluge', icon='deluge', clients=clients, login_required=True,
allowed_groups=[group[0]])
self.add(shortcut)
def init(): def init():
"""Initialize the Deluge module.""" """Initialize the Deluge module."""
@ -84,7 +90,6 @@ def init():
], is_external=True, is_enabled=is_enabled, enable=enable, ], is_external=True, is_enabled=is_enabled, enable=enable,
disable=disable) disable=disable)
if is_enabled(): if is_enabled():
add_shortcut()
app.set_enabled(True) app.set_enabled(True)
@ -99,15 +104,9 @@ def setup(helper, old_version=None):
], is_external=True, is_enabled=is_enabled, enable=enable, ], is_external=True, is_enabled=is_enabled, enable=enable,
disable=disable) disable=disable)
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
helper.call('post', app.enable) helper.call('post', app.enable)
def add_shortcut():
frontpage.add_shortcut('deluge', name, short_description, url='/deluge',
login_required=True, allowed_groups=[group[0]])
def is_enabled(): def is_enabled():
"""Return whether the module is enabled.""" """Return whether the module is enabled."""
return (action_utils.webserver_is_enabled('deluge-plinth') return (action_utils.webserver_is_enabled('deluge-plinth')
@ -117,14 +116,12 @@ def is_enabled():
def enable(): def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('deluge', ['enable']) actions.superuser_run('deluge', ['enable'])
add_shortcut()
app.enable() app.enable()
def disable(): def disable():
"""Disable the module.""" """Disable the module."""
actions.superuser_run('deluge', ['disable']) actions.superuser_run('deluge', ['disable'])
frontpage.remove_shortcut('deluge')
app.disable() app.disable()

View File

@ -87,6 +87,20 @@ class DiasporaApp(app_module.App):
parent_url_name='apps') parent_url_name='apps')
self.add(menu_item) self.add(menu_item)
shortcut = Shortcut(
'shortcut-diaspora', name, short_description=short_description,
icon='diaspora', url=None, clients=clients, login_required=True)
self.add(shortcut)
class Shortcut(frontpage.Shortcut):
"""Frontpage shortcut to use configured domain name for URL."""
def enable(self):
"""Set the proper shortcut URL when enabled."""
super().enable()
self.url = 'https://diaspora.{}'.format(get_configured_domain_name())
def init(): def init():
"""Initialize the Diaspora module.""" """Initialize the Diaspora module."""
@ -102,7 +116,6 @@ def init():
disable=disable) disable=disable)
if is_enabled(): if is_enabled():
add_shortcut()
app.set_enabled(True) app.set_enabled(True)
@ -112,7 +125,6 @@ def setup(helper, old_version=None):
helper.install(managed_packages) helper.install(managed_packages)
helper.call('custom_config', actions.superuser_run, 'diaspora', helper.call('custom_config', actions.superuser_run, 'diaspora',
['disable-ssl']) ['disable-ssl'])
helper.call('post', app.enable)
def setup_domain_name(domain_name): def setup_domain_name(domain_name):
@ -124,16 +136,7 @@ def setup_domain_name(domain_name):
], is_external=True, is_enabled=is_enabled, enable=enable, ], is_external=True, is_enabled=is_enabled, enable=enable,
disable=disable) disable=disable)
service.notify_enabled(None, True) service.notify_enabled(None, True)
add_shortcut() app.enable()
def add_shortcut():
"""Add shortcut to diaspora on the homepage."""
if is_setup():
frontpage.add_shortcut(
'diaspora', name, short_description,
url='https://diaspora.{}'.format(get_configured_domain_name()),
login_required=True)
def is_enabled(): def is_enabled():
@ -144,14 +147,12 @@ def is_enabled():
def enable(): def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('diaspora', ['enable']) actions.superuser_run('diaspora', ['enable'])
add_shortcut()
app.enable() app.enable()
def disable(): def disable():
"""Disable the module.""" """Disable the module."""
actions.superuser_run('diaspora', ['disable']) actions.superuser_run('diaspora', ['disable'])
frontpage.remove_shortcut('diaspora')
app.disable() app.disable()

View File

@ -87,6 +87,13 @@ class EjabberdApp(app_module.App):
parent_url_name='apps') parent_url_name='apps')
self.add(menu_item) self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-ejabberd', name, short_description=short_description,
icon='ejabberd', description=description,
configure_url=reverse_lazy('ejabberd:index'), clients=clients,
login_required=True)
self.add(shortcut)
def init(): def init():
"""Initialize the ejabberd module""" """Initialize the ejabberd module"""
@ -102,7 +109,6 @@ def init():
'xmpp-bosh'], is_external=True, is_enabled=is_enabled, 'xmpp-bosh'], is_external=True, is_enabled=is_enabled,
enable=enable, disable=disable) enable=enable, disable=disable)
if is_enabled(): if is_enabled():
add_shortcut()
app.set_enabled(True) app.set_enabled(True)
pre_hostname_change.connect(on_pre_hostname_change) pre_hostname_change.connect(on_pre_hostname_change)
@ -127,17 +133,9 @@ def setup(helper, old_version=None):
'xmpp-bosh'], is_external=True, is_enabled=is_enabled, 'xmpp-bosh'], is_external=True, is_enabled=is_enabled,
enable=enable, disable=disable) enable=enable, disable=disable)
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
helper.call('post', app.enable) helper.call('post', app.enable)
def add_shortcut():
frontpage.add_shortcut(
'ejabberd', name=name, short_description=short_description,
details=description, configure_url=reverse_lazy('ejabberd:index'),
login_required=True)
def is_enabled(): def is_enabled():
"""Return whether the module is enabled.""" """Return whether the module is enabled."""
return action_utils.service_is_enabled('ejabberd') return action_utils.service_is_enabled('ejabberd')
@ -146,14 +144,12 @@ def is_enabled():
def enable(): def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('ejabberd', ['enable']) actions.superuser_run('ejabberd', ['enable'])
add_shortcut()
app.enable() app.enable()
def disable(): def disable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('ejabberd', ['disable']) actions.superuser_run('ejabberd', ['disable'])
frontpage.remove_shortcut('ejabberd')
app.disable() app.disable()

View File

@ -86,6 +86,12 @@ class I2PApp(app_module.App):
'i2p:index', parent_url_name='apps') 'i2p:index', parent_url_name='apps')
self.add(menu_item) self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-i2p', name, short_description=short_description,
icon='i2p', url='/i2p/', clients=clients, login_required=True,
allowed_groups=[group[0]])
self.add(shortcut)
def init(): def init():
"""Intialize the module.""" """Intialize the module."""
@ -106,7 +112,6 @@ def init():
is_external=False, is_enabled=is_enabled, is_running=is_running) is_external=False, is_enabled=is_enabled, is_running=is_running)
if is_enabled(): if is_enabled():
add_shortcut()
app.set_enabled(True) app.set_enabled(True)
@ -152,17 +157,9 @@ def setup(helper, old_version=None):
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', proxies_service.notify_enabled, None, True) helper.call('post', proxies_service.notify_enabled, None, True)
helper.call('post', add_shortcut)
helper.call('post', app.enable) helper.call('post', app.enable)
def add_shortcut():
"""Helper method to add a shortcut to the frontpage."""
frontpage.add_shortcut('i2p', name, short_description=short_description,
url='/i2p/', login_required=True,
allowed_groups=[group[0]])
def is_running(): def is_running():
"""Return whether the service is running.""" """Return whether the service is running."""
return action_utils.service_is_running('i2p') return action_utils.service_is_running('i2p')
@ -177,14 +174,12 @@ def is_enabled():
def enable(): def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('i2p', ['enable']) actions.superuser_run('i2p', ['enable'])
add_shortcut()
app.enable() app.enable()
def disable(): def disable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('i2p', ['disable']) actions.superuser_run('i2p', ['disable'])
frontpage.remove_shortcut('i2p')
app.disable() app.disable()

View File

@ -78,6 +78,24 @@ class IkiwikiApp(app_module.App):
parent_url_name='apps') parent_url_name='apps')
self.add(menu_item) self.add(menu_item)
sites = actions.run('ikiwiki', ['get-sites']).split('\n')
sites = [name for name in sites if name != '']
for site in sites:
self.add_shortcut(site)
def add_shortcut(self, site):
"""Add an ikiwiki shortcut to frontpage."""
shortcut = frontpage.Shortcut('shortcut-ikiwiki-' + site, site,
icon='ikiwiki', url='/ikiwiki/' + site,
clients=clients)
self.add(shortcut)
return shortcut
def remove_shortcut(self, site):
"""Remove an ikiwiki shortcut from frontpage."""
component = self.remove('shortcut-ikiwiki-' + site)
component.remove() # Remove from global list.
def init(): def init():
"""Initialize the ikiwiki module.""" """Initialize the ikiwiki module."""
@ -92,7 +110,6 @@ def init():
'ikiwiki', name, ports=['http', 'https'], is_external=True, 'ikiwiki', name, ports=['http', 'https'], is_external=True,
is_enabled=is_enabled, enable=enable, disable=disable) is_enabled=is_enabled, enable=enable, disable=disable)
if is_enabled(): if is_enabled():
add_shortcuts()
app.set_enabled(True) app.set_enabled(True)
@ -106,19 +123,9 @@ def setup(helper, old_version=None):
'ikiwiki', name, ports=['http', 'https'], is_external=True, 'ikiwiki', name, ports=['http', 'https'], is_external=True,
is_enabled=is_enabled, enable=enable, disable=disable) is_enabled=is_enabled, enable=enable, disable=disable)
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcuts)
helper.call('post', app.enable) helper.call('post', app.enable)
def add_shortcuts():
sites = actions.run('ikiwiki', ['get-sites']).split('\n')
sites = [name for name in sites if name != '']
for site in sites:
frontpage.add_shortcut('ikiwiki_' + site, site, url='/ikiwiki/' + site,
login_required=False, icon='ikiwiki',
allowed_groups=[group[0]])
def is_enabled(): def is_enabled():
"""Return whether the module is enabled.""" """Return whether the module is enabled."""
return action_utils.webserver_is_enabled('ikiwiki-plinth') return action_utils.webserver_is_enabled('ikiwiki-plinth')
@ -127,14 +134,12 @@ def is_enabled():
def enable(): def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('ikiwiki', ['enable']) actions.superuser_run('ikiwiki', ['enable'])
add_shortcuts()
app.enable() app.enable()
def disable(): def disable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('ikiwiki', ['disable']) actions.superuser_run('ikiwiki', ['disable'])
frontpage.remove_shortcut('ikiwiki*')
app.disable() app.disable()

View File

@ -25,7 +25,7 @@ from django.urls import reverse_lazy
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy from django.utils.translation import ugettext_lazy
from plinth import actions, frontpage, views from plinth import actions, views
from plinth.modules import ikiwiki from plinth.modules import ikiwiki
from .forms import IkiwikiCreateForm from .forms import IkiwikiCreateForm
@ -93,9 +93,8 @@ def create(request):
form.cleaned_data['admin_password']) form.cleaned_data['admin_password'])
site = form.cleaned_data['name'].replace(' ', '') site = form.cleaned_data['name'].replace(' ', '')
frontpage.add_shortcut('ikiwiki_' + site, site, shortcut = ikiwiki.app.add_shortcut(site)
url='/ikiwiki/' + site, shortcut.enable()
login_required=False, icon='ikiwiki')
return redirect(reverse_lazy('ikiwiki:manage')) return redirect(reverse_lazy('ikiwiki:manage'))
else: else:
@ -147,8 +146,8 @@ def delete(request, name):
if request.method == 'POST': if request.method == 'POST':
try: try:
actions.superuser_run('ikiwiki', ['delete', '--name', name]) actions.superuser_run('ikiwiki', ['delete', '--name', name])
ikiwiki.app.remove_shortcut(name)
messages.success(request, _('{name} deleted.').format(name=name)) messages.success(request, _('{name} deleted.').format(name=name))
frontpage.remove_shortcut('ikiwiki_' + name)
except actions.ActionError as error: except actions.ActionError as error:
messages.error( messages.error(
request, request,
@ -157,8 +156,7 @@ def delete(request, name):
return redirect(reverse_lazy('ikiwiki:manage')) return redirect(reverse_lazy('ikiwiki:manage'))
return TemplateResponse( return TemplateResponse(request, 'ikiwiki_delete.html', {
request, 'ikiwiki_delete.html', { 'title': ikiwiki.name,
'title': ikiwiki.name, 'name': name
'name': name })
})

View File

@ -69,6 +69,13 @@ class InfinotedApp(app_module.App):
parent_url_name='apps') parent_url_name='apps')
self.add(menu_item) self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-infinoted', name, short_description=short_description,
icon='infinoted', description=description,
configure_url=reverse_lazy('infinoted:index'), clients=clients,
login_required=False)
self.add(shortcut)
def init(): def init():
"""Initialize the infinoted module.""" """Initialize the infinoted module."""
@ -82,7 +89,6 @@ def init():
'infinoted-plinth' 'infinoted-plinth'
], is_external=True, enable=enable, disable=disable) ], is_external=True, enable=enable, disable=disable)
if service.is_enabled(): if service.is_enabled():
add_shortcut()
app.set_enabled(True) app.set_enabled(True)
@ -105,28 +111,18 @@ def setup(helper, old_version=None):
], is_external=True, enable=enable, disable=disable) ], is_external=True, enable=enable, disable=disable)
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
helper.call('post', app.enable) helper.call('post', app.enable)
def add_shortcut():
frontpage.add_shortcut(
'infinoted', name, short_description=short_description, url=None,
details=description, configure_url=reverse_lazy('infinoted:index'),
login_required=False)
def enable(): def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('service', ['enable', managed_services[0]]) actions.superuser_run('service', ['enable', managed_services[0]])
add_shortcut()
app.enable() app.enable()
def disable(): def disable():
"""Disable the module.""" """Disable the module."""
actions.superuser_run('service', ['disable', managed_services[0]]) actions.superuser_run('service', ['disable', managed_services[0]])
frontpage.remove_shortcut('infinoted')
app.disable() app.disable()

View File

@ -61,6 +61,11 @@ class JSXCApp(app_module.App):
'jsxc:index', parent_url_name='apps') 'jsxc:index', parent_url_name='apps')
self.add(menu_item) self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-jsxc', name=name, short_description=short_description,
icon='jsxc', url=reverse_lazy('jsxc:jsxc'), clients=clients)
self.add(shortcut)
def init(): def init():
"""Initialize the JSXC module""" """Initialize the JSXC module"""
@ -74,7 +79,6 @@ def init():
'jsxc', name, ports=['http', 'https'], is_external=True, 'jsxc', name, ports=['http', 'https'], is_external=True,
is_enabled=is_enabled, enable=enable, disable=disable) is_enabled=is_enabled, enable=enable, disable=disable)
if is_enabled(): if is_enabled():
add_shortcut()
app.set_enabled(True) app.set_enabled(True)
@ -88,16 +92,9 @@ def setup(helper, old_version=None):
'jsxc', name, ports=['http', 'https'], is_external=True, 'jsxc', name, ports=['http', 'https'], is_external=True,
is_enabled=is_enabled, enable=enable, disable=disable) is_enabled=is_enabled, enable=enable, disable=disable)
helper.call('post', add_shortcut)
helper.call('post', app.enable) helper.call('post', app.enable)
def add_shortcut():
frontpage.add_shortcut('jsxc', name=name,
short_description=short_description,
url=reverse_lazy('jsxc:jsxc'), login_required=True)
def is_enabled(): def is_enabled():
"""Return whether the module is enabled.""" """Return whether the module is enabled."""
setup_helper = globals()['setup_helper'] setup_helper = globals()['setup_helper']
@ -105,10 +102,8 @@ def is_enabled():
def enable(): def enable():
add_shortcut()
app.enable() app.enable()
def disable(): def disable():
frontpage.remove_shortcut('jsxc')
app.disable() app.disable()

View File

@ -83,6 +83,14 @@ class MatrixSynapseApp(app_module.App):
parent_url_name='apps') parent_url_name='apps')
self.add(menu_item) self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-matrixsynapse', name,
short_description=short_description, icon='matrixsynapse',
description=description,
configure_url=reverse_lazy('matrixsynapse:index'), clients=clients,
login_required=True)
self.add(shortcut)
def init(): def init():
"""Initialize the matrix-synapse module.""" """Initialize the matrix-synapse module."""
@ -97,7 +105,6 @@ def init():
], is_external=True, is_enabled=is_enabled, enable=enable, ], is_external=True, is_enabled=is_enabled, enable=enable,
disable=disable) disable=disable)
if is_enabled(): if is_enabled():
add_shortcut()
app.set_enabled(True) app.set_enabled(True)
@ -114,18 +121,9 @@ def setup(helper, old_version=None):
helper.call('post', actions.superuser_run, 'matrixsynapse', helper.call('post', actions.superuser_run, 'matrixsynapse',
['post-install']) ['post-install'])
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
helper.call('post', app.enable) helper.call('post', app.enable)
def add_shortcut():
"""Add a shortcut to the frontpage."""
frontpage.add_shortcut('matrixsynapse', name, details=description,
short_description=short_description,
configure_url=reverse_lazy('matrixsynapse:index'),
login_required=True)
def is_setup(): def is_setup():
"""Return whether the Matrix Synapse server is setup.""" """Return whether the Matrix Synapse server is setup."""
return os.path.exists(SERVER_NAME_PATH) return os.path.exists(SERVER_NAME_PATH)
@ -139,14 +137,12 @@ def is_enabled():
def enable(): def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('matrixsynapse', ['enable']) actions.superuser_run('matrixsynapse', ['enable'])
add_shortcut()
app.enable() app.enable()
def disable(): def disable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('matrixsynapse', ['disable']) actions.superuser_run('matrixsynapse', ['disable'])
frontpage.remove_shortcut('matrixsynapse')
app.disable() app.disable()

View File

@ -65,11 +65,28 @@ class MediaWikiApp(app_module.App):
def __init__(self): def __init__(self):
"""Create components for the app.""" """Create components for the app."""
super().__init__() super().__init__()
self._private_mode = True
menu_item = menu.Menu('menu-mediawiki', name, short_description, menu_item = menu.Menu('menu-mediawiki', name, short_description,
'mediawiki', 'mediawiki:index', 'mediawiki', 'mediawiki:index',
parent_url_name='apps') parent_url_name='apps')
self.add(menu_item) self.add(menu_item)
shortcut = Shortcut('shortcut-mediawiki', name,
short_description=short_description,
icon='mediawiki', url='/mediawiki',
clients=clients, login_required=True)
self.add(shortcut)
class Shortcut(frontpage.Shortcut):
"""Frontpage shortcut for only logged users when in private mode."""
def enable(self):
"""When enabled, check if MediaWiki is in private mode."""
super().enable()
self.login_required = is_private_mode_enabled()
def init(): def init():
"""Intialize the module.""" """Intialize the module."""
@ -83,7 +100,6 @@ def init():
'mediawiki', name, ports=['http', 'https'], is_external=True, 'mediawiki', name, ports=['http', 'https'], is_external=True,
is_enabled=is_enabled, enable=enable, disable=disable) is_enabled=is_enabled, enable=enable, disable=disable)
if is_enabled(): if is_enabled():
add_shortcut()
app.set_enabled(True) app.set_enabled(True)
@ -105,17 +121,9 @@ def setup(helper, old_version=None):
ports=['http', 'https'], ports=['http', 'https'],
) )
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
helper.call('post', app.enable) helper.call('post', app.enable)
def add_shortcut():
"""Helper method to add a shortcut to the frontpage."""
frontpage.add_shortcut(
'mediawiki', name, short_description=short_description,
url='/mediawiki', login_required=is_private_mode_enabled())
def is_enabled(): def is_enabled():
"""Return whether the module is enabled.""" """Return whether the module is enabled."""
return action_utils.webserver_is_enabled('mediawiki') return action_utils.webserver_is_enabled('mediawiki')
@ -124,14 +132,12 @@ def is_enabled():
def enable(): def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('mediawiki', ['enable']) actions.superuser_run('mediawiki', ['enable'])
add_shortcut()
app.enable() app.enable()
def disable(): def disable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('mediawiki', ['disable']) actions.superuser_run('mediawiki', ['disable'])
frontpage.remove_shortcut('mediawiki')
app.disable() app.disable()

View File

@ -26,7 +26,7 @@ from django.utils.translation import ugettext as _
from plinth import actions, views from plinth import actions, views
from plinth.modules import mediawiki from plinth.modules import mediawiki
from . import (add_shortcut, is_enabled, is_private_mode_enabled, from . import (is_enabled, is_private_mode_enabled,
is_public_registration_enabled) is_public_registration_enabled)
from .forms import MediaWikiForm from .forms import MediaWikiForm
@ -110,6 +110,8 @@ class MediaWikiServiceView(views.ServiceView):
else: else:
actions.superuser_run('mediawiki', ['private-mode', 'disable']) actions.superuser_run('mediawiki', ['private-mode', 'disable'])
messages.success(self.request, _('Private mode disabled')) messages.success(self.request, _('Private mode disabled'))
if is_enabled():
add_shortcut() mediawiki.app.get('shortcut-mediawiki').login_required = \
new_config['enable_private_mode']
return super().form_valid(form) return super().form_valid(form)

View File

@ -88,6 +88,13 @@ class MinetestApp(app_module.App):
parent_url_name='apps') parent_url_name='apps')
self.add(menu_item) self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-minetest', name, short_description=short_description,
icon='minetest', description=description,
configure_url=reverse_lazy('minetest:index'), clients=clients,
login_required=False)
self.add(shortcut)
def init(): def init():
"""Initialize the module.""" """Initialize the module."""
@ -101,7 +108,6 @@ def init():
'minetest-plinth' 'minetest-plinth'
], is_external=True, enable=enable, disable=disable) ], is_external=True, enable=enable, disable=disable)
if service.is_enabled(): if service.is_enabled():
add_shortcut()
app.set_enabled(True) app.set_enabled(True)
@ -114,28 +120,18 @@ def setup(helper, old_version=None):
'minetest-plinth' 'minetest-plinth'
], is_external=True, enable=enable, disable=disable) ], is_external=True, enable=enable, disable=disable)
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
helper.call('post', app.enable) helper.call('post', app.enable)
def add_shortcut():
frontpage.add_shortcut(
'minetest', name, url=None, short_description=short_description,
details=description, configure_url=reverse_lazy('minetest:index'),
login_required=False)
def enable(): def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('service', ['enable', managed_services[0]]) actions.superuser_run('service', ['enable', managed_services[0]])
add_shortcut()
app.enable() app.enable()
def disable(): def disable():
"""Disable the module.""" """Disable the module."""
actions.superuser_run('service', ['disable', managed_services[0]]) actions.superuser_run('service', ['disable', managed_services[0]])
frontpage.remove_shortcut('minetest')
app.disable() app.disable()

View File

@ -76,6 +76,12 @@ class MLDonkeyApp(app_module.App):
parent_url_name='apps') parent_url_name='apps')
self.add(menu_item) self.add(menu_item)
shortcuts = frontpage.Shortcut(
'shortcut-mldonkey', name, short_description=short_description,
icon='mldonkey', url='/mldonkey/', login_required=True,
clients=clients, allowed_groups=[group[0]])
self.add(shortcuts)
def init(): def init():
"""Initialize the MLDonkey module.""" """Initialize the MLDonkey module."""
@ -93,7 +99,6 @@ def init():
is_running=is_running) is_running=is_running)
if is_enabled(): if is_enabled():
add_shortcut()
app.set_enabled(True) app.set_enabled(True)
@ -110,17 +115,9 @@ def setup(helper, old_version=None):
disable=disable, disable=disable,
is_running=is_running) is_running=is_running)
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
helper.call('post', app.enable) helper.call('post', app.enable)
def add_shortcut():
"""Helper method to add a shortcut to the frontpage."""
frontpage.add_shortcut(
'mldonkey', name, short_description=short_description,
url='/mldonkey/', login_required=True, allowed_groups=[group[0]])
def is_running(): def is_running():
"""Return whether the service is running.""" """Return whether the service is running."""
return action_utils.service_is_running('mldonkey-server') return action_utils.service_is_running('mldonkey-server')
@ -135,14 +132,12 @@ def is_enabled():
def enable(): def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('mldonkey', ['enable']) actions.superuser_run('mldonkey', ['enable'])
add_shortcut()
app.enable() app.enable()
def disable(): def disable():
"""Disable the module.""" """Disable the module."""
actions.superuser_run('mldonkey', ['disable']) actions.superuser_run('mldonkey', ['disable'])
frontpage.remove_shortcut('mldonkey')
app.disable() app.disable()

View File

@ -73,6 +73,12 @@ class MumbleApp(app_module.App):
'mumble:index', parent_url_name='apps') 'mumble:index', parent_url_name='apps')
self.add(menu_item) self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-mumble', name, short_description=short_description,
icon='mumble', description=description,
configure_url=reverse_lazy('mumble:index'), clients=clients)
self.add(shortcut)
def init(): def init():
"""Intialize the Mumble module.""" """Intialize the Mumble module."""
@ -87,7 +93,6 @@ def init():
], is_external=True, enable=enable, disable=disable) ], is_external=True, enable=enable, disable=disable)
if service.is_enabled(): if service.is_enabled():
add_shortcut()
app.set_enabled(True) app.set_enabled(True)
@ -109,28 +114,18 @@ def setup(helper, old_version=None):
'mumble-plinth' 'mumble-plinth'
], is_external=True, enable=enable, disable=disable) ], is_external=True, enable=enable, disable=disable)
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
helper.call('post', app.enable) helper.call('post', app.enable)
def add_shortcut():
frontpage.add_shortcut('mumble', name, short_description=short_description,
details=description,
configure_url=reverse_lazy('mumble:index'),
login_required=False)
def enable(): def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('service', ['enable', managed_services[0]]) actions.superuser_run('service', ['enable', managed_services[0]])
add_shortcut()
app.enable() app.enable()
def disable(): def disable():
"""Disable the module.""" """Disable the module."""
actions.superuser_run('service', ['disable', managed_services[0]]) actions.superuser_run('service', ['disable', managed_services[0]])
frontpage.remove_shortcut('mumble')
app.disable() app.disable()

View File

@ -70,6 +70,16 @@ class OpenVPNApp(app_module.App):
parent_url_name='apps') parent_url_name='apps')
self.add(menu_item) self.add(menu_item)
download_profile = \
format_lazy(_('<a class="btn btn-primary btn-sm" href="{link}">'
'Download Profile</a>'),
link=reverse_lazy('openvpn:profile'))
shortcut = frontpage.Shortcut(
'shortcut-openvpn', name, short_description=short_description,
icon='openvpn', description=description + [download_profile],
configure_url=reverse_lazy('openvpn:index'), login_required=True)
self.add(shortcut)
def init(): def init():
"""Initialize the OpenVPN module.""" """Initialize the OpenVPN module."""
@ -83,7 +93,6 @@ def init():
ports=['openvpn'], is_external=True) ports=['openvpn'], is_external=True)
if service.is_enabled() and is_setup(): if service.is_enabled() and is_setup():
add_shortcut()
app.set_enabled(True) app.set_enabled(True)
@ -98,22 +107,9 @@ def setup(helper, old_version=None):
enable=enable, disable=disable) enable=enable, disable=disable)
if service.is_enabled() and is_setup(): if service.is_enabled() and is_setup():
add_shortcut()
helper.call('post', app.enable) helper.call('post', app.enable)
def add_shortcut():
"""Add shortcut in frontpage."""
download_profile = \
format_lazy(_('<a class="btn btn-primary btn-sm" href="{link}">'
'Download Profile</a>'),
link=reverse_lazy('openvpn:profile'))
frontpage.add_shortcut(
'openvpn', name, short_description=short_description,
details=description + [download_profile],
configure_url=reverse_lazy('openvpn:index'), login_required=True)
def is_setup(): def is_setup():
"""Return whether the service is running.""" """Return whether the service is running."""
return actions.superuser_run('openvpn', ['is-setup']).strip() == 'true' return actions.superuser_run('openvpn', ['is-setup']).strip() == 'true'
@ -122,14 +118,12 @@ def is_setup():
def enable(): def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('service', ['enable', managed_services[0]]) actions.superuser_run('service', ['enable', managed_services[0]])
add_shortcut()
app.enable() app.enable()
def disable(): def disable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('service', ['disable', managed_services[0]]) actions.superuser_run('service', ['disable', managed_services[0]])
frontpage.remove_shortcut('openvpn')
app.disable() app.disable()

View File

@ -77,7 +77,7 @@ def setup(request):
setup_process = actions.superuser_run('openvpn', ['setup'], setup_process = actions.superuser_run('openvpn', ['setup'],
run_in_background=True) run_in_background=True)
openvpn.add_shortcut() openvpn.enable()
return redirect('openvpn:index') return redirect('openvpn:index')

View File

@ -77,6 +77,12 @@ class PrivoxyApp(app_module.App):
parent_url_name='apps') parent_url_name='apps')
self.add(menu_item) self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-privoxy', name, short_description=short_description,
icon='privoxy', description=description,
configure_url=reverse_lazy('privoxy:index'), login_required=True)
self.add(shortcut)
def init(): def init():
"""Intialize the module.""" """Intialize the module."""
@ -91,7 +97,6 @@ def init():
enable=enable, disable=disable) enable=enable, disable=disable)
if service.is_enabled(): if service.is_enabled():
add_shortcut()
app.set_enabled(True) app.set_enabled(True)
@ -105,28 +110,18 @@ def setup(helper, old_version=None):
ports=['privoxy'], is_external=False, ports=['privoxy'], is_external=False,
enable=enable, disable=disable) enable=enable, disable=disable)
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
helper.call('post', app.enable) helper.call('post', app.enable)
def add_shortcut():
frontpage.add_shortcut(
'privoxy', name, short_description=short_description,
details=description, configure_url=reverse_lazy('privoxy:index'),
login_required=True)
def enable(): def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('service', ['enable', managed_services[0]]) actions.superuser_run('service', ['enable', managed_services[0]])
add_shortcut()
app.enable() app.enable()
def disable(): def disable():
"""Disable the module.""" """Disable the module."""
actions.superuser_run('service', ['disable', managed_services[0]]) actions.superuser_run('service', ['disable', managed_services[0]])
frontpage.remove_shortcut('privoxy')
app.disable() app.disable()

View File

@ -80,6 +80,13 @@ class QuasselApp(app_module.App):
parent_url_name='apps') parent_url_name='apps')
self.add(menu_item) self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-quassel', name, short_description=short_description,
icon='quassel', description=description,
configure_url=reverse_lazy('quassel:index'), clients=clients,
login_required=True)
self.add(shortcut)
def init(): def init():
"""Initialize the quassel module.""" """Initialize the quassel module."""
@ -94,7 +101,6 @@ def init():
], is_external=True, enable=enable, disable=disable) ], is_external=True, enable=enable, disable=disable)
if service.is_enabled(): if service.is_enabled():
add_shortcut()
app.set_enabled(True) app.set_enabled(True)
@ -116,28 +122,18 @@ def setup(helper, old_version=None):
'quassel-plinth' 'quassel-plinth'
], is_external=True, enable=enable, disable=disable) ], is_external=True, enable=enable, disable=disable)
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
helper.call('post', app.enable) helper.call('post', app.enable)
def add_shortcut():
frontpage.add_shortcut(
'quassel', name, short_description=short_description,
details=description, configure_url=reverse_lazy('quassel:index'),
login_required=True)
def enable(): def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('service', ['enable', managed_services[0]]) actions.superuser_run('service', ['enable', managed_services[0]])
add_shortcut()
app.enable() app.enable()
def disable(): def disable():
"""Disable the module.""" """Disable the module."""
actions.superuser_run('service', ['disable', managed_services[0]]) actions.superuser_run('service', ['disable', managed_services[0]])
frontpage.remove_shortcut('quassel')
app.disable() app.disable()

View File

@ -84,6 +84,12 @@ class RadicaleApp(app_module.App):
parent_url_name='apps') parent_url_name='apps')
self.add(menu_item) self.add(menu_item)
shortcut = frontpage.Shortcut('shortcut-radicale', name,
short_description=short_description,
icon='radicale', url='/radicale/',
clients=clients, login_required=True)
self.add(shortcut)
def init(): def init():
"""Initialize the radicale module.""" """Initialize the radicale module."""
@ -100,7 +106,6 @@ def init():
is_running=is_running) is_running=is_running)
if is_enabled(): if is_enabled():
add_shortcut()
app.set_enabled(True) app.set_enabled(True)
@ -138,16 +143,9 @@ def setup(helper, old_version=None):
disable=disable, disable=disable,
is_running=is_running) is_running=is_running)
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
helper.call('post', app.enable) helper.call('post', app.enable)
def add_shortcut():
frontpage.add_shortcut('radicale', name,
short_description=short_description,
url='/radicale/', login_required=True)
def get_package_version(): def get_package_version():
try: try:
proc = subprocess.run(['radicale', '--version'], proc = subprocess.run(['radicale', '--version'],
@ -195,14 +193,12 @@ def is_enabled():
def enable(): def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('radicale', ['enable']) actions.superuser_run('radicale', ['enable'])
add_shortcut()
app.enable() app.enable()
def disable(): def disable():
"""Disable the module.""" """Disable the module."""
actions.superuser_run('radicale', ['disable']) actions.superuser_run('radicale', ['disable'])
frontpage.remove_shortcut('radicale')
app.disable() app.disable()

View File

@ -81,6 +81,13 @@ class ReproApp(app_module.App):
'repro:index', parent_url_name='apps') 'repro:index', parent_url_name='apps')
self.add(menu_item) self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-repro', name, short_description=short_description,
icon='repro', description=description,
configure_url=reverse_lazy('repro:index'), clients=clients,
login_required=True)
self.add(shortcut)
def init(): def init():
"""Initialize the repro module.""" """Initialize the repro module."""
@ -95,7 +102,6 @@ def init():
], is_external=True, enable=enable, disable=disable) ], is_external=True, enable=enable, disable=disable)
if service.is_enabled(): if service.is_enabled():
add_shortcut()
app.set_enabled(True) app.set_enabled(True)
@ -118,28 +124,18 @@ def setup(helper, old_version=None):
'sip', 'sips', 'rtp-plinth' 'sip', 'sips', 'rtp-plinth'
], is_external=True, enable=enable, disable=disable) ], is_external=True, enable=enable, disable=disable)
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
helper.call('post', app.enable) helper.call('post', app.enable)
def add_shortcut():
frontpage.add_shortcut('repro', name, short_description=short_description,
details=description,
configure_url=reverse_lazy('repro:index'),
login_required=True)
def enable(): def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('service', ['enable', managed_services[0]]) actions.superuser_run('service', ['enable', managed_services[0]])
add_shortcut()
app.enable() app.enable()
def disable(): def disable():
"""Disable the module.""" """Disable the module."""
actions.superuser_run('service', ['disable', managed_services[0]]) actions.superuser_run('service', ['disable', managed_services[0]])
frontpage.remove_shortcut('repro')
app.disable() app.disable()

View File

@ -75,6 +75,12 @@ class RoundcubeApp(app_module.App):
parent_url_name='apps') parent_url_name='apps')
self.add(menu_item) self.add(menu_item)
shortcut = frontpage.Shortcut('shortcut-roundcube', name,
short_description=short_description,
icon='roundcube', url='/roundcube/',
clients=clients, login_required=True)
self.add(shortcut)
def init(): def init():
"""Intialize the module.""" """Intialize the module."""
@ -89,7 +95,6 @@ def init():
is_enabled=is_enabled, enable=enable, disable=disable) is_enabled=is_enabled, enable=enable, disable=disable)
if is_enabled(): if is_enabled():
add_shortcut()
app.set_enabled(True) app.set_enabled(True)
@ -98,7 +103,6 @@ def setup(helper, old_version=None):
helper.call('pre', actions.superuser_run, 'roundcube', ['pre-install']) helper.call('pre', actions.superuser_run, 'roundcube', ['pre-install'])
helper.install(managed_packages) helper.install(managed_packages)
helper.call('post', actions.superuser_run, 'roundcube', ['setup']) helper.call('post', actions.superuser_run, 'roundcube', ['setup'])
helper.call('post', add_shortcut)
global service global service
if service is None: if service is None:
service = service_module.Service( service = service_module.Service(
@ -107,12 +111,6 @@ def setup(helper, old_version=None):
helper.call('post', app.enable) helper.call('post', app.enable)
def add_shortcut():
frontpage.add_shortcut('roundcube', name,
short_description=short_description,
url='/roundcube', login_required=True)
def is_enabled(): def is_enabled():
"""Return whether the module is enabled.""" """Return whether the module is enabled."""
return action_utils.webserver_is_enabled('roundcube') return action_utils.webserver_is_enabled('roundcube')
@ -121,14 +119,12 @@ def is_enabled():
def enable(): def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('roundcube', ['enable']) actions.superuser_run('roundcube', ['enable'])
add_shortcut()
app.enable() app.enable()
def disable(): def disable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('roundcube', ['disable']) actions.superuser_run('roundcube', ['disable'])
frontpage.remove_shortcut('roundcube')
app.disable() app.disable()

View File

@ -66,6 +66,12 @@ class SearxApp(app_module.App):
'searx:index', parent_url_name='apps') 'searx:index', parent_url_name='apps')
self.add(menu_item) self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-searx', name, short_description=short_description,
icon='searx', url='/searx/', clients=clients, login_required=True,
allowed_groups=[group[0]])
self.add(shortcut)
def init(): def init():
"""Intialize the module.""" """Intialize the module."""
@ -82,7 +88,6 @@ def init():
disable=disable) disable=disable)
if is_enabled(): if is_enabled():
add_shortcut()
app.set_enabled(True) app.set_enabled(True)
@ -99,17 +104,9 @@ def setup(helper, old_version=None):
'http', 'https' 'http', 'https'
], is_external=True, is_enabled=is_enabled, enable=enable, ], is_external=True, is_enabled=is_enabled, enable=enable,
disable=disable) disable=disable)
helper.call('post', add_shortcut)
helper.call('post', app.enable) helper.call('post', app.enable)
def add_shortcut():
"""Helper method to add a shortcut to the frontpage."""
frontpage.add_shortcut('searx', name, short_description=short_description,
url='/searx/', login_required=True,
allowed_groups=[group[0]])
def get_safe_search_setting(): def get_safe_search_setting():
"""Get the current value of the safe search setting for Seax.""" """Get the current value of the safe search setting for Seax."""
value = actions.superuser_run('searx', ['get-safe-search']) value = actions.superuser_run('searx', ['get-safe-search'])
@ -125,14 +122,12 @@ def is_enabled():
def enable(): def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('searx', ['enable']) actions.superuser_run('searx', ['enable'])
add_shortcut()
app.enable() app.enable()
def disable(): def disable():
"""Disable the module.""" """Disable the module."""
actions.superuser_run('searx', ['disable']) actions.superuser_run('searx', ['disable'])
frontpage.remove_shortcut('searx')
app.disable() app.disable()

View File

@ -63,6 +63,12 @@ class ShaarliApp(app_module.App):
parent_url_name='apps') parent_url_name='apps')
self.add(menu_item) self.add(menu_item)
shortcut = frontpage.Shortcut('shortcut-shaarli', name,
short_description=short_description,
icon='shaarli', url='/shaarli',
clients=clients, login_required=True)
self.add(shortcut)
def init(): def init():
"""Initialize the module.""" """Initialize the module."""
@ -77,7 +83,6 @@ def init():
is_enabled=is_enabled, enable=enable, disable=disable) is_enabled=is_enabled, enable=enable, disable=disable)
if is_enabled(): if is_enabled():
add_shortcut()
app.set_enabled(True) app.set_enabled(True)
@ -90,16 +95,9 @@ def setup(helper, old_version=None):
'shaarli', name, ports=['http', 'https'], is_external=True, 'shaarli', name, ports=['http', 'https'], is_external=True,
is_enabled=is_enabled, enable=enable, disable=disable) is_enabled=is_enabled, enable=enable, disable=disable)
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
helper.call('post', app.enable) helper.call('post', app.enable)
def add_shortcut():
frontpage.add_shortcut('shaarli', name,
short_description=short_description, url='/shaarli',
login_required=True)
def is_enabled(): def is_enabled():
"""Return whether the module is enabled.""" """Return whether the module is enabled."""
return action_utils.webserver_is_enabled('shaarli') return action_utils.webserver_is_enabled('shaarli')
@ -108,12 +106,10 @@ def is_enabled():
def enable(): def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('shaarli', ['enable']) actions.superuser_run('shaarli', ['enable'])
add_shortcut()
app.enable() app.enable()
def disable(): def disable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('shaarli', ['disable']) actions.superuser_run('shaarli', ['disable'])
frontpage.remove_shortcut('shaarli')
app.disable() app.disable()

View File

@ -71,6 +71,13 @@ class ShadowsocksApp(app_module.App):
parent_url_name='apps') parent_url_name='apps')
self.add(menu_item) self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-shadowsocks', name, short_description=short_description,
icon='shadowsocks', description=description,
configure_url=reverse_lazy('shadowsocks:index'),
login_required=True)
self.add(shortcut)
def init(): def init():
"""Intialize the module.""" """Intialize the module."""
@ -86,7 +93,6 @@ def init():
enable=enable, disable=disable) enable=enable, disable=disable)
if service.is_enabled(): if service.is_enabled():
add_shortcut()
app.set_enabled(True) app.set_enabled(True)
@ -104,14 +110,6 @@ def setup(helper, old_version=None):
helper.call('post', app.enable) helper.call('post', app.enable)
def add_shortcut():
"""Helper method to add a shortcut to the frontpage."""
frontpage.add_shortcut(
'shadowsocks', name, short_description=short_description,
details=description, configure_url=reverse_lazy('shadowsocks:index'),
login_required=False)
def is_enabled(): def is_enabled():
"""Return whether service is enabled.""" """Return whether service is enabled."""
return action_utils.service_is_enabled(managed_services[0]) return action_utils.service_is_enabled(managed_services[0])
@ -125,14 +123,12 @@ def is_running():
def enable(): def enable():
"""Enable service.""" """Enable service."""
actions.superuser_run('service', ['enable', managed_services[0]]) actions.superuser_run('service', ['enable', managed_services[0]])
add_shortcut()
app.enable() app.enable()
def disable(): def disable():
"""Disable service.""" """Disable service."""
actions.superuser_run('service', ['disable', managed_services[0]]) actions.superuser_run('service', ['disable', managed_services[0]])
frontpage.remove_shortcut('shadowsocks')
app.disable() app.disable()

View File

@ -79,6 +79,12 @@ class SyncthingApp(app_module.App):
parent_url_name='apps') parent_url_name='apps')
self.add(menu_item) self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-syncthing', name, short_description=short_description,
icon='syncthing', url='/syncthing/', clients=clients,
login_required=True, allowed_groups=[group[0]])
self.add(shortcut)
def init(): def init():
"""Intialize the module.""" """Intialize the module."""
@ -96,7 +102,6 @@ def init():
is_running=is_running) is_running=is_running)
if is_enabled(): if is_enabled():
add_shortcut()
app.set_enabled(True) app.set_enabled(True)
@ -112,17 +117,9 @@ def setup(helper, old_version=None):
disable=disable, disable=disable,
is_running=is_running) is_running=is_running)
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
helper.call('post', app.enable) helper.call('post', app.enable)
def add_shortcut():
"""Helper method to add a shortcut to the frontpage."""
frontpage.add_shortcut(
'syncthing', name, short_description=short_description,
url='/syncthing/', login_required=True, allowed_groups=[group[0]])
def is_running(): def is_running():
"""Return whether the service is running.""" """Return whether the service is running."""
return action_utils.service_is_running('syncthing@syncthing') return action_utils.service_is_running('syncthing@syncthing')
@ -137,14 +134,12 @@ def is_enabled():
def enable(): def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('syncthing', ['enable']) actions.superuser_run('syncthing', ['enable'])
add_shortcut()
app.enable() app.enable()
def disable(): def disable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('syncthing', ['disable']) actions.superuser_run('syncthing', ['disable'])
frontpage.remove_shortcut('syncthing')
app.disable() app.disable()

View File

@ -72,6 +72,20 @@ class TahoeApp(app_module.App):
parent_url_name='apps') parent_url_name='apps')
self.add(menu_item) self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-tahoe', name, short_description=short_description,
icon='tahoe-lafs', url=None, login_required=True)
self.add(shortcut)
class Shortcut(frontpage.Shortcut):
"""Frontpage shortcut to use configured domain name for URL."""
def enable(self):
"""Set the proper shortcut URL when enabled."""
super().enable()
self.url = 'https://{}:5678'.format(get_configured_domain_name())
def is_setup(): def is_setup():
"""Check whether Tahoe-LAFS is setup""" """Check whether Tahoe-LAFS is setup"""
@ -116,7 +130,6 @@ def init():
is_running=is_running) is_running=is_running)
if is_enabled(): if is_enabled():
add_shortcut()
app.set_enabled(True) app.set_enabled(True)
@ -144,19 +157,9 @@ def post_setup(configured_domain_name):
disable=disable, disable=disable,
is_running=is_running) is_running=is_running)
service.notify_enabled(None, True) service.notify_enabled(None, True)
add_shortcut()
app.enable() app.enable()
def add_shortcut():
"""Helper method to add a shortcut to the front page."""
# BUG: Current logo appears squashed on front page.
frontpage.add_shortcut(
'tahoe-lafs', name, short_description=short_description,
url='https://{}:5678'.format(get_configured_domain_name()),
login_required=True)
def is_running(): def is_running():
"""Return whether the service is running.""" """Return whether the service is running."""
return action_utils.service_is_running(managed_services[0]) return action_utils.service_is_running(managed_services[0])
@ -171,14 +174,12 @@ def is_enabled():
def enable(): def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('tahoe-lafs', ['enable']) actions.superuser_run('tahoe-lafs', ['enable'])
add_shortcut()
app.enable() app.enable()
def disable(): def disable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('tahoe-lafs', ['disable']) actions.superuser_run('tahoe-lafs', ['disable'])
frontpage.remove_shortcut('tahoe-lafs')
app.disable() app.disable()

View File

@ -71,6 +71,12 @@ class TransmissionApp(app_module.App):
parent_url_name='apps') parent_url_name='apps')
self.add(menu_item) self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-transmission', name, short_description=short_description,
icon='transmission', url='/transmission', clients=clients,
login_required=True, allowed_groups=[group[0]])
self.add(shortcut)
def init(): def init():
"""Initialize the Transmission module.""" """Initialize the Transmission module."""
@ -87,7 +93,6 @@ def init():
disable=disable) disable=disable)
if is_enabled(): if is_enabled():
add_shortcut()
app.set_enabled(True) app.set_enabled(True)
@ -111,16 +116,9 @@ def setup(helper, old_version=None):
], is_external=True, is_enabled=is_enabled, enable=enable, ], is_external=True, is_enabled=is_enabled, enable=enable,
disable=disable) disable=disable)
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
helper.call('post', app.enable) helper.call('post', app.enable)
def add_shortcut():
frontpage.add_shortcut(
'transmission', name, short_description=short_description,
url='/transmission', login_required=True, allowed_groups=[group[0]])
def is_enabled(): def is_enabled():
"""Return whether the module is enabled.""" """Return whether the module is enabled."""
return (action_utils.service_is_enabled('transmission-daemon') return (action_utils.service_is_enabled('transmission-daemon')
@ -130,14 +128,12 @@ def is_enabled():
def enable(): def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('transmission', ['enable']) actions.superuser_run('transmission', ['enable'])
add_shortcut()
app.enable() app.enable()
def disable(): def disable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('transmission', ['disable']) actions.superuser_run('transmission', ['disable'])
frontpage.remove_shortcut('transmission')
app.disable() app.disable()

View File

@ -77,6 +77,12 @@ class TTRSSApp(app_module.App):
'ttrss:index', parent_url_name='apps') 'ttrss:index', parent_url_name='apps')
self.add(menu_item) self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-ttrss', name, short_description=short_description,
icon='ttrss', url='/tt-rss', clients=clients, login_required=True,
allowed_groups=[group[0]])
self.add(shortcut)
def init(): def init():
"""Intialize the module.""" """Intialize the module."""
@ -93,7 +99,6 @@ def init():
disable=disable) disable=disable)
if is_enabled(): if is_enabled():
add_shortcut()
app.set_enabled(True) app.set_enabled(True)
@ -110,7 +115,6 @@ def setup(helper, old_version=None):
], is_external=True, is_enabled=is_enabled, enable=enable, ], is_external=True, is_enabled=is_enabled, enable=enable,
disable=disable) disable=disable)
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
helper.call('post', app.enable) helper.call('post', app.enable)
@ -129,13 +133,6 @@ def force_upgrade(helper, packages):
actions.superuser_run('ttrss', ['setup']) actions.superuser_run('ttrss', ['setup'])
def add_shortcut():
"""Add a shortcut to the front page."""
frontpage.add_shortcut('ttrss', name, short_description=short_description,
url='/tt-rss', login_required=True,
allowed_groups=[group[0]])
def is_enabled(): def is_enabled():
"""Return whether the module is enabled.""" """Return whether the module is enabled."""
return (action_utils.service_is_enabled('tt-rss') return (action_utils.service_is_enabled('tt-rss')
@ -145,14 +142,12 @@ def is_enabled():
def enable(): def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('ttrss', ['enable']) actions.superuser_run('ttrss', ['enable'])
add_shortcut()
app.enable() app.enable()
def disable(): def disable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('ttrss', ['disable']) actions.superuser_run('ttrss', ['disable'])
frontpage.remove_shortcut('ttrss')
app.disable() app.disable()

View File

@ -26,7 +26,7 @@
{% block container %} {% block container %}
{% if messages or details or not shortcuts %} {% if messages or selected_shortcut or not shortcuts %}
<div class="container content-container"> <div class="container content-container">
{% include 'messages.html' %} {% include 'messages.html' %}
@ -41,21 +41,21 @@
</h4> </h4>
{% endif %} {% endif %}
{% if details %} {% if selected_shortcut.description %}
{% block pagetitle %} {% block pagetitle %}
<h2>{{ details_label }}</h2> <h2>{{ selected_shortcut.name }}</h2>
{% endblock %} {% endblock %}
{% block description %} {% block description %}
{% for paragraph in details %} {% for paragraph in selected_shortcut.description %}
<p>{{ paragraph|safe }}</p> <p>{{ paragraph|safe }}</p>
{% endfor %} {% endfor %}
{% endblock %} {% endblock %}
{% include "clients.html" with clients=clients enabled=service.is_enabled %} {% include "clients.html" with clients=clients enabled=service.is_enabled %}
{% if user.is_authenticated and user_is_admin and configure_url %} {% if user.is_authenticated and user_is_admin and selected_shortcut.configure_url %}
<a class="btn btn-primary" href="{{ configure_url }}"> <a class="btn btn-primary" href="{{ selected_shortcut.configure_url }}">
{% trans "Configure &raquo;" %}</a> {% trans "Configure &raquo;" %}</a>
{% endif %} {% endif %}
{% endif %} {% endif %}
@ -68,10 +68,10 @@
<div class="row"> <div class="row">
<div class="card-list"> <div class="card-list">
{% for shortcut in shortcuts %} {% for shortcut in shortcuts %}
{% if not shortcut.hidden %} {% if shortcut.is_enabled %}
{% if user.is_authenticated or not shortcut.login_required %} {% if user.is_authenticated or not shortcut.login_required %}
<div class="card thumbnail"> <div class="card thumbnail">
{% if selected_id == shortcut.id %} {% if selected_shortcut.component_id == shortcut.component_id %}
<a href="{{ shortcut.url }}" class="nav-link active" data-turbolinks="false"> <a href="{{ shortcut.url }}" class="nav-link active" data-turbolinks="false">
{% else %} {% else %}
<a href="{{ shortcut.url }}" class="nav-link" data-turbolinks="false"> <a href="{{ shortcut.url }}" class="nav-link" data-turbolinks="false">

View File

@ -58,6 +58,23 @@ def test_app_add():
assert return_value == app assert return_value == app
def test_app_remove(app_with_components):
"""Test removing a component from an App."""
app = app_with_components
component = app.components['test-leader-1']
assert app.remove('test-leader-1') == component
assert 'test-leader-1' not in app.components
def test_get(app_with_components):
"""Test retrieving a component from an App."""
app = app_with_components
component = app.components['test-leader-1']
assert app.get('test-leader-1') == component
with pytest.raises(KeyError):
app.get('x-invalid-component')
def test_app_enable(app_with_components): def test_app_enable(app_with_components):
"""Test that enabling an app enables components.""" """Test that enabling an app enables components."""
app_with_components.disable() app_with_components.disable()

View File

@ -0,0 +1,143 @@
#
# This file is part of FreedomBox.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
"""
Test module for frontpage.
"""
from unittest.mock import patch
import pytest
from plinth.frontpage import Shortcut
# pylint: disable=protected-access
@pytest.fixture(name='clean_global_shotcuts', autouse=True)
def fixture_clean_global_shortcuts():
"""Ensure that global list of shortcuts is clean."""
Shortcut._all_shortcuts = {}
def test_shortcut_init_with_arguments():
"""Test initializing shortcut component without arguments."""
with pytest.raises(ValueError):
Shortcut(None, None)
shortcut = Shortcut('test-component', 'test-name')
assert shortcut.component_id == 'test-component'
assert shortcut.name == 'test-name'
assert shortcut.short_description is None
assert shortcut.url == '?selected=test-component'
assert shortcut.icon is None
assert shortcut.description is None
assert shortcut.configure_url is None
assert shortcut.clients is None
assert not shortcut.login_required
assert shortcut.allowed_groups is None
assert Shortcut._all_shortcuts['test-component'] == shortcut
def test_shortcut_init():
"""Test initializing shortcut component."""
clients = ['client1', 'client2']
allowed_groups = ['group1', 'group2']
shortcut = Shortcut('test-component', name='test-name',
short_description='test-short-description',
url='test-url', icon='test-icon',
description='test-description',
configure_url='test-configure-url', clients=clients,
login_required=True, allowed_groups=allowed_groups)
assert shortcut.short_description == 'test-short-description'
assert shortcut.url == 'test-url'
assert shortcut.icon == 'test-icon'
assert shortcut.description == 'test-description'
assert shortcut.configure_url == 'test-configure-url'
assert shortcut.clients == clients
assert shortcut.login_required
assert shortcut.allowed_groups == set(allowed_groups)
def test_shortcut_remove():
"""Test removing a shortcut global list of shortcuts."""
shortcut = Shortcut('test-component', None)
shortcut.remove()
with pytest.raises(KeyError):
del Shortcut._all_shortcuts['test-component']
@pytest.fixture(name='common_shortcuts')
def fixture_common_shortcuts(clean_global_shotcuts):
"""Create some common shortcuts."""
shortcuts = [
Shortcut('anon-web-app-component-1', 'name1', 'short4', url='url1'),
Shortcut('group1-web-app-component-1', 'Name2', 'Short3', url='url2',
allowed_groups=['group1']),
Shortcut('group2-web-app-component-1', 'name3', 'short2', url='url3',
allowed_groups=['group2']),
Shortcut('anon-non-web-app-component-1', 'name4', 'short1', url=None),
]
return shortcuts
def test_shortcut_list_sorting(common_shortcuts):
"""Test listing shortcuts in sorted order."""
cuts = common_shortcuts
return_list = Shortcut.list()
assert return_list == [cuts[0], cuts[1], cuts[2], cuts[3]]
return_list = Shortcut.list(sort_by='name')
assert return_list == [cuts[0], cuts[1], cuts[2], cuts[3]]
return_list = Shortcut.list(sort_by='short_description')
assert return_list == [cuts[3], cuts[2], cuts[1], cuts[0]]
def test_shortcut_list_web_apps_only(common_shortcuts):
"""Test listing only web app shortcuts."""
cuts = common_shortcuts
return_list = Shortcut.list()
assert Shortcut.list() == [cuts[0], cuts[1], cuts[2], cuts[3]]
return_list = Shortcut.list(web_apps_only=False)
assert return_list == [cuts[0], cuts[1], cuts[2], cuts[3]]
return_list = Shortcut.list(web_apps_only=True)
assert return_list == [cuts[0], cuts[1], cuts[2]]
@patch('plinth.actions.superuser_run')
def test_shortcut_list_with_username(superuser_run, common_shortcuts):
"""Test listing for particular users."""
cuts = common_shortcuts
return_list = Shortcut.list()
assert return_list == [cuts[0], cuts[1], cuts[2], cuts[3]]
superuser_run.return_value = 'admin'
return_list = Shortcut.list(username='admin')
assert return_list == [cuts[0], cuts[1], cuts[2], cuts[3]]
superuser_run.return_value = 'group1'
return_list = Shortcut.list(username='user1')
assert return_list == [cuts[0], cuts[1], cuts[3]]
superuser_run.return_value = 'group1\ngroup2'
return_list = Shortcut.list(username='user2')
assert return_list == [cuts[0], cuts[1], cuts[2], cuts[3]]

View File

@ -44,14 +44,15 @@ REDIRECT_FIELD_NAME = 'next'
def index(request): def index(request):
"""Serve the main index page.""" """Serve the main index page."""
username = str(request.user) if request.user.is_authenticated else None username = str(request.user) if request.user.is_authenticated else None
shortcuts = frontpage.get_shortcuts(username) shortcuts = frontpage.Shortcut.list(username)
selection = request.GET.get('selected') selected = request.GET.get('selected')
details, details_label, configure_url = None, None, None selected_shortcut = [
if selection in frontpage.shortcuts: shortcut
details = frontpage.shortcuts[selection]['details'] for shortcut in shortcuts
details_label = frontpage.shortcuts[selection]['label'] if shortcut.component_id == selected
configure_url = frontpage.shortcuts[selection]['configure_url'] ]
selected_shortcut = selected_shortcut[0] if selected_shortcut else None
disk_views.warn_about_low_disk_space(request) disk_views.warn_about_low_disk_space(request)
@ -59,10 +60,7 @@ def index(request):
request, 'index.html', { request, 'index.html', {
'title': _('FreedomBox'), 'title': _('FreedomBox'),
'shortcuts': shortcuts, 'shortcuts': shortcuts,
'selected_id': selection, 'selected_shortcut': selected_shortcut,
'details': details,
'details_label': details_label,
'configure_url': configure_url
}) })