Introduce component architecture and menu component

- Introduce base class for all apps that will contain components. With
  unittests.

- Introduce base classes for components. With unittests.

- Turn Menu class into an app component.

  - Further cleanup Menu class.

  - Update tests.

  - Maintain a global list of menu items and look them up easily. Generalize
    such that subsubmenus can later be merged into Menu class.

  - Cleanup scope of main menu initialization.

  - Use None instead of empty strings for various values. Ensure that
    printing short_description does not show 'None' in output.

  - Use enable/disable instead of promote/demote.

- Use menu component in all apps.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
This commit is contained in:
Sunil Mohan Adapa 2019-05-08 17:34:54 -07:00
parent 0eee8ddf65
commit b96d901071
No known key found for this signature in database
GPG Key ID: 43EA1CFF0AA7C5F2
61 changed files with 1527 additions and 567 deletions

130
plinth/app.py Normal file
View File

@ -0,0 +1,130 @@
#
# This file is part of Plinth.
#
# 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/>.
#
"""
Base class for all Freedombox applications.
"""
import collections
class App:
"""Implement common functionality for an app.
An app is composed of components which actually performs various tasks. App
itself delegates tasks for individual components. Applications can show a
variation their behavior by choosing which components to have and by
customizing the components themselves.
"""
def __init__(self):
"""Initialize the app object."""
self.components = collections.OrderedDict()
def add(self, component):
"""Add a component to an app."""
self.components[component.component_id] = component
return self
def enable(self):
"""Enable all the components of the app."""
for component in self.components.values():
component.enable()
def disable(self):
"""Enable all the components of the app."""
for component in self.components.values():
component.disable()
def is_enabled(self):
"""Return whether all the leader components are enabled."""
return all((component.is_enabled()
for component in self.components.values()
if component.is_leader))
def set_enabled(self, enabled):
"""Update the status of all follower components.
Do not query or update the status of the leader components.
"""
for component in self.components.values():
if not component.is_leader:
if enabled:
component.enable()
else:
component.disable()
class Component:
"""Interface for an app component."""
is_leader = False
def __init__(self, component_id):
"""Intialize the component."""
if not component_id:
raise ValueError('Invalid component ID')
self.component_id = component_id
def enable(self):
"""Enable the component."""
def disable(self):
"""Disable the component."""
class FollowerComponent(Component):
"""Interface for an app component that follows other components.
These components of the app don't determine if the app is enabled or not.
"""
is_leader = False
def __init__(self, component_id, is_enabled=False):
"""Intialize the component."""
super().__init__(component_id)
self._is_enabled = is_enabled
def is_enabled(self):
"""Return whether the component is enabled."""
return self._is_enabled
def enable(self):
"""Enable the component."""
self._is_enabled = True
def disable(self):
"""Disable the component."""
self._is_enabled = False
class LeaderComponent(Component):
"""Interface for an app component that decides the state of the app.
These components determine if the app is enabled or not.
"""
is_leader = True
def is_enabled(self):
"""Return if the component is enabled."""
raise NotImplementedError

View File

@ -22,8 +22,7 @@ Django context processors to provide common data to templates.
from django.utils.translation import ugettext as _, ugettext_noop
import re
from plinth import cfg
from plinth.menu import main_menu
from plinth import cfg, menu
from plinth.utils import is_user_admin
@ -41,7 +40,7 @@ def common(request):
active_menu_urls = [request.path[:index + 1] for index in slash_indices]
return {
'cfg': cfg,
'submenu': main_menu.active_item(request),
'submenu': menu.main_menu.active_item(request),
'active_menu_urls': active_menu_urls,
'box_name': _(cfg.box_name),
'user_is_admin': is_user_admin(request, True)

View File

@ -17,106 +17,88 @@
from django.urls import reverse, reverse_lazy
from plinth.utils import format_lazy
from plinth import app
class Menu(object):
"""One menu item."""
class Menu(app.FollowerComponent):
"""Component to manage a single menu item."""
def __init__(self, name="", short_description="", icon="", url="#",
order=50):
_all_menus = {}
def __init__(self, component_id, name=None, short_description=None,
icon=None, url_name=None, url_args=None, url_kwargs=None,
parent_url_name=None, order=50):
"""Initialize a new menu item with basic properties.
icon is the icon to be displayed for the menu item.
Choose from the Fork Awesome set:
https://forkawesome.github.io/Fork-Awesome/icons/
name is the label of the menu item.
url is the url location that will be activated when the menu
item is selected.
short_description is an optional description shown on the menu item.
order is the numerical rank of this item within the menu.
Lower order items appear closest to the top/left of the menu.
By convention, we use the spectrum between 0 and 100 to rank
orders, but feel free to disregard that. If you need more
granularity, don't bother renumbering things. Feel free to
use fractional orders.
icon is the icon to be displayed for the menu item. Choose from the
Fork Awesome set: https://forkawesome.github.io/Fork-Awesome/icons/
url_name is the name of url location that will be activated when the
menu item is selected. This is not optional. url_args and url_kwargs
are sent to reverse() when resolving url from url_name.
parent_url_name optionally specifies the menu item under which this
menu item should become a child.
order is the numerical rank of this item within the menu. Lower order
items appear closest to the top/left of the menu. By convention, we use
the spectrum between 0 and 100 to rank orders, but feel free to
disregard that. If you need more granularity, don't bother renumbering
things. Feel free to use fractional orders.
"""
super().__init__(component_id)
if not url_name:
raise ValueError('Valid url_name is expected')
url = reverse_lazy(url_name, args=url_args, kwargs=url_kwargs)
self.name = name
self.short_description = short_description
self.icon = icon
self.url = url
self.order = order
self.secondary = True
# TODO: With an ordered dictionary for self.items we could access the
# items by their URL directly instead of searching for them each time,
# which we do currently with the 'get' method
self.items = []
def get(self, urlname, url_args=None, url_kwargs=None):
# Add self to parent menu item
if parent_url_name:
parent_menu = self.get(parent_url_name)
parent_menu.items.append(self)
# Add self to global list of menu items
self._all_menus[url] = self
@classmethod
def get(cls, urlname, url_args=None, url_kwargs=None):
"""Return a menu item with given URL name."""
url = reverse(urlname, args=url_args, kwargs=url_kwargs)
for item in self.items:
if str(item.url) == url:
return item
raise KeyError('Menu item not found')
return cls._all_menus[url]
def sorted_items(self):
"""Return menu items in sorted order according to current locale."""
return sorted(self.items, key=lambda x: (x.order, x.name.lower()))
def add_urlname(self, name, icon, urlname, short_description="", order=50,
url_args=None, url_kwargs=None):
"""Add a named URL to the menu (via add_item).
url_args and url_kwargs will be passed on to Django reverse().
"""
url = reverse_lazy(urlname, args=url_args, kwargs=url_kwargs)
item = Menu(name=name, short_description=short_description, icon=icon,
url=url, order=order)
self.items.append(item)
return item
def active_item(self, request):
"""Return the first active item (e.g. submenu) that is found."""
for item in self.items:
if request.path.startswith(str(item.url)):
return item
def promote_item(self, urlname, url_args=None, url_kwargs=None):
"""Promote a secondary item to an item."""
found_item = None
url = reverse(urlname, args=url_args, kwargs=url_kwargs)
for item in self.items:
if str(item.url) == url:
found_item = item
if found_item:
found_item.secondary = False
else:
raise KeyError('Menu item not found')
def demote_item(self, urlname, url_args=None, url_kwargs=None):
"""Demote an item to a secondary item."""
found_item = None
url = reverse(urlname, args=url_args, kwargs=url_kwargs)
for item in self.items:
if str(item.url) == url:
found_item = item
if found_item:
found_item.secondary = True
else:
raise KeyError('Menu item not found')
return None
main_menu = Menu()
main_menu = None
def init():
"""Create main menu and other essential menus."""
main_menu.add_urlname('', 'fa-download', 'apps')
main_menu.add_urlname('', 'fa-cog', 'system')
global main_menu
main_menu = Menu('menu-index', url_name='index')
Menu('menu-apps', icon='fa-download', url_name='apps',
parent_url_name='index')
Menu('menu-system', icon='fa-cog', url_name='system',
parent_url_name='index')

View File

@ -20,9 +20,10 @@ FreedomBox app for service discovery.
from django.utils.translation import ugettext_lazy as _
from plinth import actions
from plinth import app as app_module
from plinth import cfg, menu
from plinth import service as service_module
from plinth import actions, cfg
from plinth.menu import main_menu
from plinth.utils import format_lazy
from plinth.views import ServiceView
@ -55,11 +56,25 @@ service = None
manual_page = 'ServiceDiscovery'
app = None
class AvahiApp(app_module.App):
"""FreedomBox app for Avahi."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-avahi', name, None, 'fa-compass',
'avahi:index', parent_url_name='system')
self.add(menu_item)
def init():
"""Intialize the service discovery module."""
menu = main_menu.get('system')
menu.add_urlname(name, 'fa-compass', 'avahi:index')
global app
app = AvahiApp()
app.set_enabled(True)
global service # pylint: disable=W0603
service = service_module.Service(managed_services[0], name, ports=['mdns'],

View File

@ -24,8 +24,9 @@ import os
from django.utils.text import get_valid_filename
from django.utils.translation import ugettext_lazy as _
from plinth import actions, cfg
from plinth.menu import main_menu
from plinth import actions
from plinth import app as app_module
from plinth import cfg, menu
from plinth.utils import format_lazy
from . import api
@ -46,24 +47,43 @@ manual_page = 'Backups'
MANIFESTS_FOLDER = '/var/lib/plinth/backups-manifests/'
ROOT_REPOSITORY = '/var/lib/freedombox/borgbackup'
ROOT_REPOSITORY_NAME = format_lazy(_('{box_name} storage'),
box_name=cfg.box_name)
ROOT_REPOSITORY_NAME = format_lazy(
_('{box_name} storage'), box_name=cfg.box_name)
ROOT_REPOSITORY_UUID = 'root'
# session variable name that stores when a backup file should be deleted
SESSION_PATH_VARIABLE = 'fbx-backups-upload-path'
app = None
class BackupsApp(app_module.App):
"""FreedomBox app for backup and restore."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-backups', name, None, 'fa-files-o',
'backups:index', parent_url_name='system')
self.add(menu_item)
def init():
"""Intialize the module."""
menu = main_menu.get('system')
menu.add_urlname(name, 'fa-files-o', 'backups:index')
global app
app = BackupsApp()
global service
setup_helper = globals()['setup_helper']
if setup_helper.get_state() != 'needs-setup':
app.set_enabled(True)
def setup(helper, old_version=None):
"""Install and configure the module."""
helper.install(managed_packages)
helper.call('post', actions.superuser_run, 'backups', ['setup', '--path',
ROOT_REPOSITORY])
helper.call('post', actions.superuser_run, 'backups',
['setup', '--path', ROOT_REPOSITORY])
helper.call('post', app.enable)
def _backup_handler(packet, encryption_passphrase=None):
@ -102,8 +122,8 @@ def _restore_exported_archive_handler(packet, encryption_passphrase=None):
"""Perform restore operation on packet."""
locations = {'directories': packet.directories, 'files': packet.files}
locations_data = json.dumps(locations)
actions.superuser_run('backups', ['restore-exported-archive', '--path',
packet.path],
actions.superuser_run('backups',
['restore-exported-archive', '--path', packet.path],
input=locations_data.encode())
@ -111,8 +131,9 @@ def restore_archive_handler(packet, encryption_passphrase=None):
"""Perform restore operation on packet."""
locations = {'directories': packet.directories, 'files': packet.files}
locations_data = json.dumps(locations)
arguments = ['restore-archive', '--path', packet.path, '--destination',
'/']
arguments = [
'restore-archive', '--path', packet.path, '--destination', '/'
]
if encryption_passphrase:
arguments += ['--encryption-passphrase', encryption_passphrase]
actions.superuser_run('backups', arguments, input=locations_data.encode())

View File

@ -22,11 +22,10 @@ import re
from django.utils.translation import ugettext_lazy as _
from plinth import actions
from plinth import action_utils
from plinth import cfg
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import cfg, menu
from plinth import service as service_module
from plinth.menu import main_menu
from plinth.utils import format_lazy
from .manifest import backup
@ -84,17 +83,32 @@ listen-on-v6 { any; };
};
'''
app = None
class BindApp(app_module.App):
"""FreedomBox app for Bind."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-bind', name, short_description,
'fa-globe-w', 'bind:index',
parent_url_name='system')
self.add(menu_item)
def init():
"""Intialize the BIND module."""
menu = main_menu.get('system')
menu.add_urlname(name, 'fa-globe-w', 'bind:index', short_description)
global app
app = BindApp()
global service
setup_helper = globals()['setup_helper']
if setup_helper.get_state() != 'needs-setup':
service = service_module.Service(managed_services[0], name,
ports=['dns'], is_external=False)
app.set_enabled(True) # XXX: Perform better check
def setup(helper, old_version=None):
@ -107,6 +121,7 @@ def setup(helper, old_version=None):
enable=enable, disable=disable)
helper.call('post', service.notify_enabled, None, True)
helper.call('post', actions.superuser_run, 'bind', ['setup'])
helper.call('post', app.enable)
def force_upgrade(helper, _packages):
@ -117,11 +132,13 @@ def force_upgrade(helper, _packages):
def enable():
"""Enable the module."""
actions.superuser_run('service', ['enable', managed_services[0]])
app.enable()
def disable():
"""Disable the module."""
actions.superuser_run('service', ['disable', managed_services[0]])
app.disable()
def diagnose():

View File

@ -21,9 +21,10 @@ FreedomBox app to configure Cockpit.
from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions, cfg, frontpage
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import cfg, frontpage, menu
from plinth import service as service_module
from plinth.menu import main_menu
from plinth.modules import names
from plinth.signals import domain_added, domain_removed, domainname_change
from plinth.utils import format_lazy
@ -59,12 +60,25 @@ service = None
manual_page = 'Cockpit'
app = None
class CockpitApp(app_module.App):
"""FreedomBox app for Cockpit."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-cockpit', name, short_description,
'fa-wrench', 'cockpit:index',
parent_url_name='system')
self.add(menu_item)
def init():
"""Intialize the module."""
menu = main_menu.get('system')
menu.add_urlname(name, 'fa-wrench', 'cockpit:index',
short_description)
global app
app = CockpitApp()
global service
setup_helper = globals()['setup_helper']
@ -76,6 +90,7 @@ def init():
if is_enabled():
add_shortcut()
app.set_enabled(True)
domain_added.connect(on_domain_added)
domain_removed.connect(on_domain_removed)
@ -117,12 +132,14 @@ def enable():
"""Enable the module."""
actions.superuser_run('cockpit', ['enable'])
add_shortcut()
app.enable()
def disable():
"""Disable the module."""
actions.superuser_run('cockpit', ['disable'])
frontpage.remove_shortcut('cockpit')
app.disable()
def diagnose():

View File

@ -25,7 +25,8 @@ import augeas
from django.utils.translation import ugettext_lazy
from plinth import actions
from plinth.menu import main_menu
from plinth import app as app_module
from plinth import menu
from plinth.modules import firewall
from plinth.modules.names import SERVICES
from plinth.signals import domain_added
@ -45,6 +46,20 @@ APACHE_HOMEPAGE_CONFIG = os.path.join(APACHE_CONF_ENABLED_DIR,
FREEDOMBOX_APACHE_CONFIG = os.path.join(APACHE_CONF_ENABLED_DIR,
'freedombox.conf')
app = None
class ConfigApp(app_module.App):
"""FreedomBox app for basic system configuration."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-config', ugettext_lazy('Configure'), None,
'fa-cog', 'config:index',
parent_url_name='system')
self.add(menu_item)
def get_domainname():
"""Return the domainname"""
@ -89,8 +104,9 @@ def get_home_page():
def init():
"""Initialize the module"""
menu = main_menu.get('system')
menu.add_urlname(ugettext_lazy('Configure'), 'fa-cog', 'config:index')
global app
app = ConfigApp()
app.set_enabled(True)
# Register domain with Name Services module.
domainname = get_domainname()

View File

@ -20,9 +20,10 @@ Plinth module to configure coquelicot.
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import frontpage, menu
from plinth import service as service_module
from plinth import action_utils, actions, frontpage
from plinth.menu import main_menu
from .manifest import backup, clients
@ -52,12 +53,25 @@ service = None
manual_page = 'Coquelicot'
app = None
class CoquelicotApp(app_module.App):
"""FreedomBox app for Coquelicot."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-coquelicot', name, short_description,
'coquelicot', 'coquelicot:index',
parent_url_name='apps')
self.add(menu_item)
def init():
"""Intialize the module."""
menu = main_menu.get('apps')
menu.add_urlname(name, 'coquelicot', 'coquelicot:index',
short_description)
global app
app = CoquelicotApp()
global service
setup_helper = globals()['setup_helper']
@ -70,7 +84,7 @@ def init():
if is_enabled():
add_shortcut()
menu.promote_item('coquelicot:index')
app.set_enabled(True)
def setup(helper, old_version=None):
@ -87,8 +101,7 @@ def setup(helper, old_version=None):
is_running=is_running)
helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
menu = main_menu.get('apps')
helper.call('post', menu.promote_item, 'coquelicot:index')
helper.call('post', app.enable)
def add_shortcut():
@ -113,16 +126,14 @@ def enable():
"""Enable the module."""
actions.superuser_run('coquelicot', ['enable'])
add_shortcut()
menu = main_menu.get('apps')
menu.promote_item('coquelicot:index')
app.enable()
def disable():
"""Disable the module."""
actions.superuser_run('coquelicot', ['disable'])
frontpage.remove_shortcut('coquelicot')
menu = main_menu.get('apps')
menu.demote_item('coquelicot:index')
app.disable()
def get_current_max_file_size():

View File

@ -22,8 +22,9 @@ import subprocess
from django.utils.translation import ugettext_lazy as _
from plinth import app as app_module
from plinth import menu
from plinth import service as service_module
from plinth.menu import main_menu
from .manifest import backup
@ -46,11 +47,25 @@ manual_page = 'DateTime'
service = None
app = None
class DateTimeApp(app_module.App):
"""FreedomBox app for date and time."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-datetime', name, None, 'fa-clock-o',
'datetime:index', parent_url_name='system')
self.add(menu_item)
def init():
"""Intialize the date/time module."""
menu = main_menu.get('system')
menu.add_urlname(name, 'fa-clock-o', 'datetime:index')
global app
app = DateTimeApp()
app.set_enabled(True)
global service
setup_helper = globals()['setup_helper']
@ -67,6 +82,7 @@ def setup(helper, old_version=None):
is_external=True)
service.enable()
helper.call('post', service.notify_enabled, None, True)
helper.call('post', app.enable)
def diagnose():

View File

@ -20,9 +20,10 @@ FreedomBox app to configure a Deluge web client.
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import frontpage, menu
from plinth import service as service_module
from plinth import action_utils, actions, frontpage
from plinth.menu import main_menu
from plinth.modules.users import register_group
from .manifest import backup, clients
@ -55,11 +56,24 @@ clients = clients
manual_page = 'Deluge'
app = None
class DelugeApp(app_module.App):
"""FreedomBox app for Deluge."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-deluge', name, short_description, 'deluge',
'deluge:index', parent_url_name='apps')
self.add(menu_item)
def init():
"""Initialize the Deluge module."""
menu = main_menu.get('apps')
menu.add_urlname(name, 'deluge', 'deluge:index', short_description)
global app
app = DelugeApp()
register_group(group)
global service
@ -71,7 +85,7 @@ def init():
disable=disable)
if is_enabled():
add_shortcut()
menu.promote_item('deluge:index')
app.set_enabled(True)
def setup(helper, old_version=None):
@ -86,8 +100,7 @@ def setup(helper, old_version=None):
disable=disable)
helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
menu = main_menu.get('apps')
helper.call('post', menu.promote_item, 'deluge:index')
helper.call('post', app.enable)
def add_shortcut():
@ -105,16 +118,14 @@ def enable():
"""Enable the module."""
actions.superuser_run('deluge', ['enable'])
add_shortcut()
menu = main_menu.get('apps')
menu.promote_item('deluge:index')
app.enable()
def disable():
"""Disable the module."""
actions.superuser_run('deluge', ['disable'])
frontpage.remove_shortcut('deluge')
menu = main_menu.get('apps')
menu.demote_item('deluge:index')
app.disable()
def diagnose():

View File

@ -21,7 +21,8 @@ FreedomBox app for system diagnostics.
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils
from plinth.menu import main_menu
from plinth import app as app_module
from plinth import menu
from .manifest import backup
@ -39,11 +40,25 @@ description = [
manual_page = 'Diagnostics'
app = None
class DiagnosticsApp(app_module.App):
"""FreedomBox app for diagnostics."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-diagnostics', name, None, 'fa-heartbeat',
'diagnostics:index', parent_url_name='system')
self.add(menu_item)
def init():
"""Initialize the module"""
menu = main_menu.get('system')
menu.add_urlname(name, 'fa-heartbeat', 'diagnostics:index')
global app
app = DiagnosticsApp()
app.set_enabled(True)
def diagnose():

View File

@ -16,13 +16,14 @@
import os
import augeas
from django.utils.translation import ugettext_lazy as _
import augeas
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import frontpage, menu
from plinth import service as service_module
from plinth import action_utils, actions, frontpage
from plinth.errors import DomainNotRegisteredError
from plinth.menu import main_menu
from plinth.utils import format_lazy
domain_name_file = "/etc/diaspora/domain_name"
@ -72,23 +73,37 @@ description = [
from .manifest import clients # isort:skip
clients = clients
app = None
class DiasporaApp(app_module.App):
"""FreedomBox app for Diaspora."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-diaspora', name, short_description,
'diaspora', 'diaspora:index',
parent_url_name='apps')
self.add(menu_item)
def init():
"""Initialize the Diaspora module."""
menu = main_menu.get('apps')
menu.add_urlname(name, 'fa-diaspora', 'diaspora:index', short_description)
global app
app = DiasporaApp()
global service
setup_helper = globals()['setup_helper']
if setup_helper.get_state() != 'needs-setup':
service = service_module.Service(
managed_services[0], name, ports=['http', 'https'],
is_external=True, is_enabled=is_enabled, enable=enable,
disable=disable)
service = service_module.Service(managed_services[0], name, ports=[
'http', 'https'
], is_external=True, is_enabled=is_enabled, enable=enable,
disable=disable)
if is_enabled():
add_shortcut()
menu.promote_item('diaspora:index')
app.set_enabled(True)
def setup(helper, old_version=None):
@ -97,8 +112,7 @@ def setup(helper, old_version=None):
helper.install(managed_packages)
helper.call('custom_config', actions.superuser_run, 'diaspora',
['disable-ssl'])
menu = main_menu.get('apps')
helper.call('post', menu.promote_item, 'diaspora:index')
helper.call('post', app.enable)
def setup_domain_name(domain_name):
@ -131,16 +145,14 @@ def enable():
"""Enable the module."""
actions.superuser_run('diaspora', ['enable'])
add_shortcut()
menu = main_menu.get('apps')
menu.promote_item('diaspora:index')
app.enable()
def disable():
"""Disable the module."""
actions.superuser_run('diaspora', ['disable'])
frontpage.remove_shortcut('diaspora')
menu = main_menu.get('apps')
menu.demote_item('diaspora:index')
app.disable()
def is_user_registrations_enabled():
@ -172,8 +184,9 @@ def diagnose():
action_utils.diagnose_url('http://diaspora.localhost', kind='6',
check_certificate=False))
results.append(
action_utils.diagnose_url('http://diaspora.{}'.format(
get_configured_domain_name()), kind='4', check_certificate=False))
action_utils.diagnose_url(
'http://diaspora.{}'.format(get_configured_domain_name()),
kind='4', check_certificate=False))
return results

View File

@ -20,8 +20,9 @@ FreedomBox app to configure ez-ipupdate client.
from django.utils.translation import ugettext_lazy as _
from plinth import actions, cfg
from plinth.menu import main_menu
from plinth import actions
from plinth import app as app_module
from plinth import cfg, menu
from plinth.modules import firewall
from plinth.modules.names import SERVICES
from plinth.signals import domain_added
@ -57,11 +58,24 @@ reserved_usernames = ['ez-ipupd']
manual_page = 'DynamicDNS'
app = None
class DynamicDNSApp(app_module.App):
"""FreedomBox app for Dynamic DNS."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-dynamicdns', name, None, 'fa-refresh',
'dynamicdns:index', parent_url_name='system')
self.add(menu_item)
def init():
"""Initialize the module."""
menu = main_menu.get('system')
menu.add_urlname(name, 'fa-refresh', 'dynamicdns:index')
global app
app = DynamicDNSApp()
current_status = get_status()
if current_status['enabled']:
services = get_enabled_services(current_status['dynamicdns_domain'])
@ -69,6 +83,7 @@ def init():
sender='dynamicdns', domain_type='dynamicdnsservice',
name=current_status['dynamicdns_domain'],
description=_('Dynamic DNS Service'), services=services)
app.set_enabled(True)
def setup(helper, old_version=None):

View File

@ -23,9 +23,10 @@ import logging
from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions, cfg, frontpage
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import cfg, frontpage, menu
from plinth import service as service_module
from plinth.menu import main_menu
from plinth.modules import config
from plinth.signals import (domainname_change, post_hostname_change,
pre_hostname_change)
@ -72,11 +73,25 @@ port_forwarding_info = [
logger = logging.getLogger(__name__)
app = None
class EjabberdApp(app_module.App):
"""FreedomBox app for ejabberd."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-ejabberd', name, short_description,
'ejabberd', 'ejabberd:index',
parent_url_name='apps')
self.add(menu_item)
def init():
"""Initialize the ejabberd module"""
menu = main_menu.get('apps')
menu.add_urlname(name, 'ejabberd', 'ejabberd:index', short_description)
global app
app = EjabberdApp()
global service
setup_helper = globals()['setup_helper']
@ -88,7 +103,7 @@ def init():
enable=enable, disable=disable)
if is_enabled():
add_shortcut()
menu.promote_item('ejabberd:index')
app.set_enabled(True)
pre_hostname_change.connect(on_pre_hostname_change)
post_hostname_change.connect(on_post_hostname_change)
@ -113,8 +128,7 @@ def setup(helper, old_version=None):
enable=enable, disable=disable)
helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
menu = main_menu.get('apps')
helper.call('post', menu.promote_item, 'ejabberd:index')
helper.call('post', app.enable)
def add_shortcut():
@ -133,16 +147,14 @@ def enable():
"""Enable the module."""
actions.superuser_run('ejabberd', ['enable'])
add_shortcut()
menu = main_menu.get('apps')
menu.promote_item('ejabberd:index')
app.enable()
def disable():
"""Enable the module."""
actions.superuser_run('ejabberd', ['disable'])
frontpage.remove_shortcut('ejabberd')
menu = main_menu.get('apps')
menu.demote_item('ejabberd:index')
app.disable()
def on_pre_hostname_change(sender, old_hostname, new_hostname, **kwargs):

View File

@ -23,8 +23,9 @@ import logging
from django.utils.translation import ugettext_lazy as _
import plinth.service as service_module
from plinth import actions, cfg
from plinth.menu import main_menu
from plinth import actions
from plinth import app as app_module
from plinth import cfg, menu
from plinth.signals import service_enabled
from plinth.utils import Version, format_lazy
@ -52,11 +53,25 @@ LOGGER = logging.getLogger(__name__)
_port_details = {}
app = None
class FirewallApp(app_module.App):
"""FreedomBox app for Firewall."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-firewall', name, None, 'fa-shield',
'firewall:index', parent_url_name='system')
self.add(menu_item)
def init():
"""Initailze firewall module"""
menu = main_menu.get('system')
menu.add_urlname(name, 'fa-shield', 'firewall:index')
global app
app = FirewallApp()
app.set_enabled(True)
service_enabled.connect(on_service_enabled)

View File

@ -28,21 +28,43 @@ from django.template.response import TemplateResponse
from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy
from plinth import __version__, actions, cfg
from plinth.menu import main_menu
from plinth import __version__, actions
from plinth import app as app_module
from plinth import cfg, menu
app = None
class HelpApp(app_module.App):
"""FreedomBox app for showing help."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-help', ugettext_lazy('Documentation'),
None, 'fa-book', 'help:index',
parent_url_name='index')
self.add(menu_item)
menu_item = menu.Menu('menu-help-manual', ugettext_lazy('Manual'),
None, 'fa-info-circle', 'help:manual',
parent_url_name='help:index', order=10)
self.add(menu_item)
menu_item = menu.Menu('menu-help-download-manual',
ugettext_lazy('Download Manual'), None,
'fa-download', 'help:download-manual',
parent_url_name='help:index', order=15)
self.add(menu_item)
menu_item = menu.Menu('menu-help-about', ugettext_lazy('About'), None,
'fa-star', 'help:about',
parent_url_name='help:index', order=100)
self.add(menu_item)
def init():
"""Initialize the Help module"""
menu = main_menu.add_urlname(
ugettext_lazy('Documentation'), 'fa-book', 'help:index')
menu.add_urlname(
ugettext_lazy('Manual'), 'fa-info-circle', 'help:manual', order=10)
menu.add_urlname(
ugettext_lazy('Download Manual'), 'fa-download',
'help:download-manual', order=15)
menu.add_urlname(
ugettext_lazy('About'), 'fa-star', 'help:about', order=100)
global app
app = HelpApp()
app.set_enabled(True)
def index(request):

View File

@ -20,9 +20,10 @@ FreedomBox app to configure I2P.
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions, frontpage
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import frontpage, menu
from plinth import service as service_module
from plinth.menu import main_menu
from plinth.modules.i2p.resources import FAVORITES
from plinth.modules.users import register_group
@ -72,11 +73,24 @@ tunnels_to_manage = {
'Irc2P': 'i2p-irc-freedombox'
}
app = None
class I2PApp(app_module.App):
"""FreedomBox app for I2P."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-i2p', name, short_description, 'i2p',
'i2p:index', parent_url_name='apps')
self.add(menu_item)
def init():
"""Intialize the module."""
menu = main_menu.get('apps')
menu.add_urlname(name, 'i2p', 'i2p:index', short_description)
global app
app = I2PApp()
register_group(group)
global service, proxies_service
@ -93,7 +107,7 @@ def init():
if is_enabled():
add_shortcut()
menu.promote_item('i2p:index')
app.set_enabled(True)
def setup(helper, old_version=None):
@ -118,7 +132,6 @@ def setup(helper, old_version=None):
helper.call('post', actions.superuser_run, 'i2p', args)
# Tunnels to all interfaces
for tunnel in tunnels_to_manage:
helper.call('post', actions.superuser_run, 'i2p', [
@ -140,8 +153,7 @@ def setup(helper, old_version=None):
helper.call('post', service.notify_enabled, None, True)
helper.call('post', proxies_service.notify_enabled, None, True)
helper.call('post', add_shortcut)
menu = main_menu.get('apps')
helper.call('post', menu.promote_item, 'i2p:index')
helper.call('post', app.enable)
def add_shortcut():
@ -166,16 +178,14 @@ def enable():
"""Enable the module."""
actions.superuser_run('i2p', ['enable'])
add_shortcut()
menu = main_menu.get('apps')
menu.promote_item('i2p:index')
app.enable()
def disable():
"""Enable the module."""
actions.superuser_run('i2p', ['disable'])
frontpage.remove_shortcut('i2p')
menu = main_menu.get('apps')
menu.demote_item('i2p:index')
app.disable()
def diagnose():

View File

@ -21,9 +21,10 @@ FreedomBox app to configure ikiwiki.
from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions, cfg, frontpage
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import cfg, frontpage, menu
from plinth import service as service_module
from plinth.menu import main_menu
from plinth.modules.users import register_group
from plinth.utils import format_lazy
@ -63,11 +64,25 @@ group = ('wiki', _('View and edit wiki applications'))
manual_page = 'Ikiwiki'
app = None
class IkiwikiApp(app_module.App):
"""FreedomBox app for Ikiwiki."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-ikiwiki', name, short_description,
'ikiwiki', 'ikiwiki:index',
parent_url_name='apps')
self.add(menu_item)
def init():
"""Initialize the ikiwiki module."""
menu = main_menu.get('apps')
menu.add_urlname(name, 'ikiwiki', 'ikiwiki:index', short_description)
global app
app = IkiwikiApp()
register_group(group)
global service
@ -78,7 +93,7 @@ def init():
is_enabled=is_enabled, enable=enable, disable=disable)
if is_enabled():
add_shortcuts()
menu.promote_item('ikiwiki:index')
app.set_enabled(True)
def setup(helper, old_version=None):
@ -92,8 +107,7 @@ def setup(helper, old_version=None):
is_enabled=is_enabled, enable=enable, disable=disable)
helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcuts)
menu = main_menu.get('apps')
helper.call('post', menu.promote_item, 'ikiwiki:index')
helper.call('post', app.enable)
def add_shortcuts():
@ -114,16 +128,14 @@ def enable():
"""Enable the module."""
actions.superuser_run('ikiwiki', ['enable'])
add_shortcuts()
menu = main_menu.get('apps')
menu.promote_item('ikiwiki:index')
app.enable()
def disable():
"""Enable the module."""
actions.superuser_run('ikiwiki', ['disable'])
frontpage.remove_shortcut('ikiwiki*')
menu = main_menu.get('apps')
menu.demote_item('ikiwiki:index')
app.disable()
def diagnose():

View File

@ -21,14 +21,13 @@ FreedomBox app for infinoted.
from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from plinth import actions
from plinth import action_utils
from plinth import cfg
from plinth import frontpage
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import cfg, frontpage, menu
from plinth import service as service_module
from plinth.menu import main_menu
from plinth.utils import format_lazy
from plinth.views import ServiceView
from .manifest import backup, clients
version = 1
@ -56,11 +55,25 @@ clients = clients
port_forwarding_info = [('TCP', 6523)]
app = None
class InfinotedApp(app_module.App):
"""FreedomBox app for infinoted."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-infinoted', name, short_description,
'infinoted', 'infinoted:index',
parent_url_name='apps')
self.add(menu_item)
def init():
"""Initialize the infinoted module."""
menu = main_menu.get('apps')
menu.add_urlname(name, 'infinoted', 'infinoted:index', short_description)
global app
app = InfinotedApp()
global service
setup_helper = globals()['setup_helper']
@ -70,7 +83,7 @@ def init():
], is_external=True, enable=enable, disable=disable)
if service.is_enabled():
add_shortcut()
menu.promote_item('infinoted:index')
app.set_enabled(True)
class InfinotedServiceView(ServiceView):
@ -93,8 +106,7 @@ def setup(helper, old_version=None):
helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
menu = main_menu.get('apps')
helper.call('post', menu.promote_item, 'infinoted:index')
helper.call('post', app.enable)
def add_shortcut():
@ -108,16 +120,14 @@ def enable():
"""Enable the module."""
actions.superuser_run('service', ['enable', managed_services[0]])
add_shortcut()
menu = main_menu.get('apps')
menu.promote_item('infinoted:index')
app.enable()
def disable():
"""Disable the module."""
actions.superuser_run('service', ['disable', managed_services[0]])
frontpage.remove_shortcut('infinoted')
menu = main_menu.get('apps')
menu.demote_item('infinoted:index')
app.disable()
def diagnose():

View File

@ -23,9 +23,9 @@ import logging
from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from plinth import frontpage
from plinth import app as app_module
from plinth import frontpage, menu
from plinth import service as service_module
from plinth.menu import main_menu
from .manifest import backup, clients
@ -48,11 +48,24 @@ service = None
logger = logging.getLogger(__name__)
app = None
class JSXCApp(app_module.App):
"""FreedomBox app for JSXC."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-jsxc', name, short_description, 'jsxc',
'jsxc:index', parent_url_name='apps')
self.add(menu_item)
def init():
"""Initialize the JSXC module"""
menu = main_menu.get('apps')
menu.add_urlname(name, 'jsxc', 'jsxc:index', short_description)
global app
app = JSXCApp()
global service
setup_helper = globals()['setup_helper']
@ -62,7 +75,7 @@ def init():
is_enabled=is_enabled, enable=enable, disable=disable)
if is_enabled():
add_shortcut()
menu.promote_item('jsxc:index')
app.set_enabled(True)
def setup(helper, old_version=None):
@ -76,8 +89,7 @@ def setup(helper, old_version=None):
is_enabled=is_enabled, enable=enable, disable=disable)
helper.call('post', add_shortcut)
menu = main_menu.get('apps')
helper.call('post', menu.promote_item, 'jsxc:index')
helper.call('post', app.enable)
def add_shortcut():
@ -94,11 +106,9 @@ def is_enabled():
def enable():
add_shortcut()
menu = main_menu.get('apps')
menu.promote_item('jsxc:index')
app.enable()
def disable():
frontpage.remove_shortcut('jsxc')
menu = main_menu.get('apps')
menu.demote_item('jsxc:index')
app.disable()

View File

@ -23,9 +23,10 @@ import logging
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions, cfg, module_loader
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import cfg, menu, module_loader
from plinth.errors import ActionError
from plinth.menu import main_menu
from plinth.modules import config, names
from plinth.signals import domain_added, domain_removed, domainname_change
from plinth.utils import format_lazy
@ -67,11 +68,27 @@ MODULES_WITH_HOOKS = ['ejabberd', 'matrixsynapse']
LIVE_DIRECTORY = '/etc/letsencrypt/live/'
logger = logging.getLogger(__name__)
app = None
class LetsEncryptApp(app_module.App):
"""FreedomBox app for Let's Encrypt."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-letsencrypt', name, short_description,
'fa-lock', 'letsencrypt:index',
parent_url_name='system')
self.add(menu_item)
def init():
"""Intialize the module."""
menu = main_menu.get('system')
menu.add_urlname(name, 'fa-lock', 'letsencrypt:index', short_description)
global app
app = LetsEncryptApp()
app.set_enabled(True)
domainname_change.connect(on_domainname_change)
domain_added.connect(on_domain_added)
domain_removed.connect(on_domain_removed)
@ -108,8 +125,8 @@ def enable_renewal_management(domain):
try:
actions.superuser_run('letsencrypt', ['manage_hooks', 'enable'])
logger.info(
_('Certificate renewal management enabled for {domain}.')
.format(domain=domain))
_('Certificate renewal management enabled for {domain}.').
format(domain=domain))
except ActionError as exception:
logger.error(
_('Failed to enable certificate renewal management for '
@ -188,8 +205,8 @@ def on_domain_removed(sender, domain_type, name='', **kwargs):
return True
except ActionError as exception:
logger.warn(
('Failed to revoke certificate for domain {domain}: {error}')
.format(domain=name, error=exception.args[2]))
('Failed to revoke certificate for domain {domain}: {error}'
).format(domain=name, error=exception.args[2]))
return False

View File

@ -25,9 +25,10 @@ from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from ruamel.yaml.util import load_yaml_guess_indent
from plinth import action_utils, actions, frontpage
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import frontpage, menu
from plinth import service as service_module
from plinth.menu import main_menu
from .manifest import backup, clients
@ -68,12 +69,25 @@ logger = logging.getLogger(__name__)
SERVER_NAME_PATH = "/etc/matrix-synapse/conf.d/server_name.yaml"
CONFIG_FILE_PATH = '/etc/matrix-synapse/homeserver.yaml'
app = None
class MatrixSynapseApp(app_module.App):
"""FreedomBox app for Matrix Synapse."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-matrixsynapse', name, short_description,
'matrixsynapse', 'matrixsynapse:index',
parent_url_name='apps')
self.add(menu_item)
def init():
"""Initialize the matrix-synapse module."""
menu = main_menu.get('apps')
menu.add_urlname(name, 'matrixsynapse', 'matrixsynapse:index',
short_description)
global app
app = MatrixSynapseApp()
global service
setup_helper = globals()['setup_helper']
@ -84,7 +98,7 @@ def init():
disable=disable)
if is_enabled():
add_shortcut()
menu.promote_item('matrixsynapse:index')
app.set_enabled(True)
def setup(helper, old_version=None):
@ -101,8 +115,7 @@ def setup(helper, old_version=None):
['post-install'])
helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
menu = main_menu.get('apps')
helper.call('post', menu.promote_item, 'matrixsynapse:index')
helper.call('post', app.enable)
def add_shortcut():
@ -127,16 +140,14 @@ def enable():
"""Enable the module."""
actions.superuser_run('matrixsynapse', ['enable'])
add_shortcut()
menu = main_menu.get('apps')
menu.promote_item('matrixsynapse:index')
app.enable()
def disable():
"""Enable the module."""
actions.superuser_run('matrixsynapse', ['disable'])
frontpage.remove_shortcut('matrixsynapse')
menu = main_menu.get('apps')
menu.demote_item('matrixsynapse:index')
app.disable()
def diagnose():

View File

@ -20,9 +20,10 @@ FreedomBox app to configure MediaWiki.
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import frontpage, menu
from plinth import service as service_module
from plinth import action_utils, actions, frontpage
from plinth.menu import main_menu
from .manifest import backup, clients
@ -55,11 +56,25 @@ manual_page = 'MediaWiki'
clients = clients
app = None
class MediaWikiApp(app_module.App):
"""FreedomBox app for MediaWiki."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-mediawiki', name, short_description,
'mediawiki', 'mediawiki:index',
parent_url_name='apps')
self.add(menu_item)
def init():
"""Intialize the module."""
menu = main_menu.get('apps')
menu.add_urlname(name, 'mediawiki', 'mediawiki:index', short_description)
global app
app = MediaWikiApp()
global service
setup_helper = globals()['setup_helper']
@ -69,7 +84,7 @@ def init():
is_enabled=is_enabled, enable=enable, disable=disable)
if is_enabled():
add_shortcut()
menu.promote_item('mediawiki:index')
app.set_enabled(True)
def setup(helper, old_version=None):
@ -91,8 +106,7 @@ def setup(helper, old_version=None):
)
helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
menu = main_menu.get('apps')
helper.call('post', menu.promote_item, 'mediawiki:index')
helper.call('post', app.enable)
def add_shortcut():
@ -111,16 +125,14 @@ def enable():
"""Enable the module."""
actions.superuser_run('mediawiki', ['enable'])
add_shortcut()
menu = main_menu.get('apps')
menu.promote_item('mediawiki:index')
app.enable()
def disable():
"""Enable the module."""
actions.superuser_run('mediawiki', ['disable'])
frontpage.remove_shortcut('mediawiki')
menu = main_menu.get('apps')
menu.demote_item('mediawiki:index')
app.disable()
def diagnose():

View File

@ -22,9 +22,10 @@ import augeas
from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import cfg, frontpage, menu
from plinth import service as service_module
from plinth import action_utils, actions, cfg, frontpage
from plinth.menu import main_menu
from plinth.utils import format_lazy
from .manifest import backup, clients
@ -73,11 +74,25 @@ reserved_usernames = ['Debian-minetest']
CONFIG_FILE = '/etc/minetest/minetest.conf'
AUG_PATH = '/files' + CONFIG_FILE + '/.anon'
app = None
class MinetestApp(app_module.App):
"""FreedomBox app for Minetest."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-minetest', name, short_description,
'minetest', 'minetest:index',
parent_url_name='apps')
self.add(menu_item)
def init():
"""Initialize the module."""
menu = main_menu.get('apps')
menu.add_urlname(name, 'minetest', 'minetest:index', short_description)
global app
app = MinetestApp()
global service
setup_helper = globals()['setup_helper']
@ -87,7 +102,7 @@ def init():
], is_external=True, enable=enable, disable=disable)
if service.is_enabled():
add_shortcut()
menu.promote_item('minetest:index')
app.set_enabled(True)
def setup(helper, old_version=None):
@ -100,8 +115,7 @@ def setup(helper, old_version=None):
], is_external=True, enable=enable, disable=disable)
helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
menu = main_menu.get('apps')
helper.call('post', menu.promote_item, 'minetest:index')
helper.call('post', app.enable)
def add_shortcut():
@ -115,16 +129,14 @@ def enable():
"""Enable the module."""
actions.superuser_run('service', ['enable', managed_services[0]])
add_shortcut()
menu = main_menu.get('apps')
menu.promote_item('minetest:index')
app.enable()
def disable():
"""Disable the module."""
actions.superuser_run('service', ['disable', managed_services[0]])
frontpage.remove_shortcut('minetest')
menu = main_menu.get('apps')
menu.demote_item('minetest:index')
app.disable()
def diagnose():

View File

@ -20,9 +20,10 @@ FreedomBox app for mldonkey.
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions, cfg, frontpage
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import cfg, frontpage, menu
from plinth import service as service_module
from plinth.menu import main_menu
from plinth.modules.users import register_group
from plinth.utils import format_lazy
@ -61,11 +62,25 @@ service = None
manual_page = 'MLDonkey'
app = None
class MLDonkeyApp(app_module.App):
"""FreedomBox app for MLDonkey."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-mldonkey', name, short_description,
'mldonkey', 'mldonkey:index',
parent_url_name='apps')
self.add(menu_item)
def init():
"""Initialize the MLDonkey module."""
menu = main_menu.get('apps')
menu.add_urlname(name, 'mldonkey', 'mldonkey:index', short_description)
global app
app = MLDonkeyApp()
register_group(group)
global service
@ -79,7 +94,7 @@ def init():
if is_enabled():
add_shortcut()
menu.promote_item('mldonkey:index')
app.set_enabled(True)
def setup(helper, old_version=None):
@ -96,8 +111,7 @@ def setup(helper, old_version=None):
is_running=is_running)
helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
menu = main_menu.get('apps')
helper.call('post', menu.promote_item, 'mldonkey:index')
helper.call('post', app.enable)
def add_shortcut():
@ -122,16 +136,14 @@ def enable():
"""Enable the module."""
actions.superuser_run('mldonkey', ['enable'])
add_shortcut()
menu = main_menu.get('apps')
menu.promote_item('mldonkey:index')
app.enable()
def disable():
"""Disable the module."""
actions.superuser_run('mldonkey', ['disable'])
frontpage.remove_shortcut('mldonkey')
menu = main_menu.get('apps')
menu.demote_item('mldonkey:index')
app.disable()
def diagnose():

View File

@ -20,7 +20,8 @@ FreedomBox app for monkeysphere.
from django.utils.translation import ugettext_lazy as _
from plinth.menu import main_menu
from plinth import app as app_module
from plinth import menu
from .manifest import backup
@ -53,12 +54,26 @@ manual_page = "Monkeysphere"
reserved_usernames = ['monkeysphere']
app = None
class MonkeysphereApp(app_module.App):
"""FreedomBox app for Monkeysphere."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-monkeysphere', name, None,
'fa-certificate', 'monkeysphere:index',
parent_url_name='system')
self.add(menu_item)
def init():
"""Initialize the monkeysphere module."""
menu = main_menu.get('system')
menu.add_urlname(
_('Monkeysphere'), 'fa-certificate', 'monkeysphere:index')
global app
app = MonkeysphereApp()
app.set_enabled(True)
def setup(helper, old_version=None):

View File

@ -21,9 +21,10 @@ FreedomBox app to configure Mumble server.
from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import frontpage, menu
from plinth import service as service_module
from plinth import action_utils, actions, frontpage
from plinth.menu import main_menu
from plinth.views import ServiceView
from .manifest import backup, clients
@ -59,11 +60,24 @@ port_forwarding_info = [
('UDP', 64738),
]
app = None
class MumbleApp(app_module.App):
"""FreedomBox app for Mumble."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-mumble', name, short_description, 'mumble',
'mumble:index', parent_url_name='apps')
self.add(menu_item)
def init():
"""Intialize the Mumble module."""
menu = main_menu.get('apps')
menu.add_urlname(name, 'mumble', 'mumble:index', short_description)
global app
app = MumbleApp()
global service
setup_helper = globals()['setup_helper']
@ -74,7 +88,7 @@ def init():
if service.is_enabled():
add_shortcut()
menu.promote_item('mumble:index')
app.set_enabled(True)
class MumbleServiceView(ServiceView):
@ -96,8 +110,7 @@ def setup(helper, old_version=None):
], is_external=True, enable=enable, disable=disable)
helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
menu = main_menu.get('apps')
helper.call('post', menu.promote_item, 'mumble:index')
helper.call('post', app.enable)
def add_shortcut():
@ -111,16 +124,14 @@ def enable():
"""Enable the module."""
actions.superuser_run('service', ['enable', managed_services[0]])
add_shortcut()
menu = main_menu.get('apps')
menu.promote_item('mumble:index')
app.enable()
def disable():
"""Disable the module."""
actions.superuser_run('service', ['disable', managed_services[0]])
frontpage.remove_shortcut('mumble')
menu = main_menu.get('apps')
menu.demote_item('mumble:index')
app.disable()
def diagnose():

View File

@ -22,8 +22,8 @@ import logging
from django.utils.translation import ugettext_lazy as _
from plinth import cfg
from plinth.menu import main_menu
from plinth import app as app_module
from plinth import cfg, menu
from plinth.signals import domain_added, domain_removed
from plinth.utils import format_lazy
@ -57,11 +57,25 @@ description = [
'connections through the given name.'), box_name=(cfg.box_name))
]
app = None
class NamesApp(app_module.App):
"""FreedomBox app for names."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-names', name, None, 'fa-tags',
'names:index', parent_url_name='system')
self.add(menu_item)
def init():
"""Initialize the names module."""
menu = main_menu.get('system')
menu.add_urlname(name, 'fa-tags', 'names:index')
global app
app = NamesApp()
app.set_enabled(True)
domain_added.connect(on_domain_added)
domain_removed.connect(on_domain_removed)

View File

@ -23,8 +23,9 @@ from logging import Logger
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions, network
from plinth.menu import main_menu
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import menu, network
version = 1
@ -38,17 +39,32 @@ logger = Logger(__name__)
manual_page = 'Networks'
app = None
class NetworksApp(app_module.App):
"""FreedomBox app for Networks."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-networks', name, None, 'fa-signal',
'networks:index', parent_url_name='system')
self.add(menu_item)
def init():
"""Initialize the Networks module."""
menu = main_menu.get('system')
menu.add_urlname(name, 'fa-signal', 'networks:index')
global app
app = NetworksApp()
app.set_enabled(True)
def setup(helper, old_version=None):
"""Install and configure the module."""
helper.install(managed_packages)
actions.superuser_run('networks')
helper.call('post', app.enable)
def diagnose():

View File

@ -21,9 +21,10 @@ FreedomBox app to configure OpenVPN server.
from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions, cfg, frontpage
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import cfg, frontpage, menu
from plinth import service as service_module
from plinth.menu import main_menu
from plinth.utils import format_lazy
from .manifest import backup
@ -55,11 +56,25 @@ manual_page = 'OpenVPN'
port_forwarding_info = [('UDP', 1194)]
app = None
class OpenVPNApp(app_module.App):
"""FreedomBox app for OpenVPN."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-openvpn', name, short_description,
'openvpn', 'openvpn:index',
parent_url_name='apps')
self.add(menu_item)
def init():
"""Initialize the OpenVPN module."""
menu = main_menu.get('apps')
menu.add_urlname(name, 'openvpn', 'openvpn:index', short_description)
global app
app = OpenVPNApp()
global service
setup_helper = globals()['setup_helper']
@ -69,7 +84,7 @@ def init():
if service.is_enabled() and is_setup():
add_shortcut()
menu.promote_item('openvpn:index')
app.set_enabled(True)
def setup(helper, old_version=None):
@ -84,8 +99,7 @@ def setup(helper, old_version=None):
if service.is_enabled() and is_setup():
add_shortcut()
menu = main_menu.get('apps')
helper.call('post', menu.promote_item, 'openvpn:index')
helper.call('post', app.enable)
def add_shortcut():
@ -109,16 +123,14 @@ def enable():
"""Enable the module."""
actions.superuser_run('service', ['enable', managed_services[0]])
add_shortcut()
menu = main_menu.get('apps')
menu.promote_item('openvpn:index')
app.enable()
def disable():
"""Enable the module."""
actions.superuser_run('service', ['disable', managed_services[0]])
frontpage.remove_shortcut('openvpn')
menu = main_menu.get('apps')
menu.demote_item('openvpn:index')
app.disable()
def diagnose():

View File

@ -20,8 +20,8 @@ FreedomBox app to configure PageKite.
from django.utils.translation import ugettext_lazy as _
from plinth import cfg
from plinth.menu import main_menu
from plinth import app as app_module
from plinth import cfg, menu
from plinth.utils import format_lazy
from . import utils
@ -76,12 +76,30 @@ description = [
manual_page = 'PageKite'
app = None
class PagekiteApp(app_module.App):
"""FreedomBox app for Pagekite."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-pagekite', name, short_description,
'fa-flag', 'pagekite:index',
parent_url_name='system')
self.add(menu_item)
def init():
"""Intialize the PageKite module"""
menu = main_menu.get('system')
menu.add_urlname(name, 'fa-flag', 'pagekite:index',
short_description)
global app
app = PagekiteApp()
global service
setup_helper = globals()['setup_helper']
if setup_helper.get_state() != 'needs-setup':
app.set_enabled(True) # XXX: Perform more proper check
# Register kite name with Name Services module.
utils.update_names_module(initial_registration=True)
@ -90,3 +108,4 @@ def init():
def setup(helper, old_version=None):
"""Install and configure the module."""
helper.install(managed_packages)
helper.call('post', app.enable)

View File

@ -21,9 +21,10 @@ FreedomBox app to configure Privoxy.
from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import cfg, frontpage, menu
from plinth import service as service_module
from plinth import action_utils, actions, cfg, frontpage
from plinth.menu import main_menu
from plinth.utils import format_lazy
from plinth.views import ServiceView
@ -62,11 +63,25 @@ service = None
manual_page = 'Privoxy'
app = None
class PrivoxyApp(app_module.App):
"""FreedomBox app for Privoxy."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-privoxy', name, short_description,
'privoxy', 'privoxy:index',
parent_url_name='apps')
self.add(menu_item)
def init():
"""Intialize the module."""
menu = main_menu.get('apps')
menu.add_urlname(name, 'privoxy', 'privoxy:index', short_description)
global app
app = PrivoxyApp()
global service
setup_helper = globals()['setup_helper']
@ -77,7 +92,7 @@ def init():
if service.is_enabled():
add_shortcut()
menu.promote_item('privoxy:index')
app.set_enabled(True)
def setup(helper, old_version=None):
@ -91,8 +106,7 @@ def setup(helper, old_version=None):
enable=enable, disable=disable)
helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
menu = main_menu.get('apps')
helper.call('post', menu.promote_item, 'privoxy:index')
helper.call('post', app.enable)
def add_shortcut():
@ -106,16 +120,14 @@ def enable():
"""Enable the module."""
actions.superuser_run('service', ['enable', managed_services[0]])
add_shortcut()
menu = main_menu.get('apps')
menu.promote_item('privoxy:index')
app.enable()
def disable():
"""Disable the module."""
actions.superuser_run('service', ['disable', managed_services[0]])
frontpage.remove_shortcut('privoxy')
menu = main_menu.get('apps')
menu.demote_item('privoxy:index')
app.disable()
class PrivoxyServiceView(ServiceView):

View File

@ -21,9 +21,10 @@ FreedomBox app for Quassel.
from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import cfg, frontpage, menu
from plinth import service as service_module
from plinth import action_utils, actions, cfg, frontpage
from plinth.menu import main_menu
from plinth.utils import format_lazy
from plinth.views import ServiceView
@ -65,11 +66,25 @@ manual_page = 'Quassel'
port_forwarding_info = [('TCP', 4242)]
app = None
class QuasselApp(app_module.App):
"""FreedomBox app for Quassel."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-quassel', name, short_description,
'quassel', 'quassel:index',
parent_url_name='apps')
self.add(menu_item)
def init():
"""Initialize the quassel module."""
menu = main_menu.get('apps')
menu.add_urlname(name, 'quassel', 'quassel:index', short_description)
global app
app = QuasselApp()
global service
setup_helper = globals()['setup_helper']
@ -80,7 +95,7 @@ def init():
if service.is_enabled():
add_shortcut()
menu.promote_item('quassel:index')
app.set_enabled(True)
class QuasselServiceView(ServiceView):
@ -102,8 +117,7 @@ def setup(helper, old_version=None):
], is_external=True, enable=enable, disable=disable)
helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
menu = main_menu.get('apps')
helper.call('post', menu.promote_item, 'quassel:index')
helper.call('post', app.enable)
def add_shortcut():
@ -117,16 +131,14 @@ def enable():
"""Enable the module."""
actions.superuser_run('service', ['enable', managed_services[0]])
add_shortcut()
menu = main_menu.get('apps')
menu.promote_item('quassel:index')
app.enable()
def disable():
"""Disable the module."""
actions.superuser_run('service', ['disable', managed_services[0]])
frontpage.remove_shortcut('quassel')
menu = main_menu.get('apps')
menu.demote_item('quassel:index')
app.disable()
def diagnose():

View File

@ -22,13 +22,14 @@ import logging
import subprocess
from distutils.version import LooseVersion as LV
from apt.cache import Cache
import augeas
from apt.cache import Cache
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions, cfg, frontpage
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import cfg, frontpage, menu
from plinth import service as service_module
from plinth.menu import main_menu
from plinth.utils import format_lazy
from .manifest import backup, clients
@ -69,11 +70,25 @@ CONFIG_FILE = '/etc/radicale/config'
VERSION_2 = LV('2')
app = None
class RadicaleApp(app_module.App):
"""FreedomBox app for Radicale."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-radicale', name, short_description,
'radicale', 'radicale:index',
parent_url_name='apps')
self.add(menu_item)
def init():
"""Initialize the radicale module."""
menu = main_menu.get('apps')
menu.add_urlname(name, 'radicale', 'radicale:index', short_description)
global app
app = RadicaleApp()
global service
setup_helper = globals()['setup_helper']
@ -86,7 +101,7 @@ def init():
if is_enabled():
add_shortcut()
menu.promote_item('radicale:index')
app.set_enabled(True)
def setup(helper, old_version=None):
@ -124,8 +139,7 @@ def setup(helper, old_version=None):
is_running=is_running)
helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
menu = main_menu.get('apps')
helper.call('post', menu.promote_item, 'radicale:index')
helper.call('post', app.enable)
def add_shortcut():
@ -182,16 +196,14 @@ def enable():
"""Enable the module."""
actions.superuser_run('radicale', ['enable'])
add_shortcut()
menu = main_menu.get('apps')
menu.promote_item('radicale:index')
app.enable()
def disable():
"""Disable the module."""
actions.superuser_run('radicale', ['disable'])
frontpage.remove_shortcut('radicale')
menu = main_menu.get('apps')
menu.demote_item('radicale:index')
app.disable()
def load_augeas():

View File

@ -21,9 +21,10 @@ FreedomBox app for repro.
from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import frontpage, menu
from plinth import service as service_module
from plinth import action_utils, actions, frontpage
from plinth.menu import main_menu
from plinth.views import ServiceView
from .manifest import backup, clients
@ -67,12 +68,24 @@ manual_page = 'Repro'
port_forwarding_info = [('UDP', '1024-65535')]
app = None
class ReproApp(app_module.App):
"""FreedomBox app for Repro."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-repro', name, short_description, 'repro',
'repro:index', parent_url_name='apps')
self.add(menu_item)
def init():
"""Initialize the repro module."""
menu = main_menu.get('apps')
menu.add_urlname(name, 'repro', 'repro:index',
short_description)
global app
app = ReproApp()
global service
setup_helper = globals()['setup_helper']
@ -83,7 +96,7 @@ def init():
if service.is_enabled():
add_shortcut()
menu.promote_item('repro:index')
app.set_enabled(True)
class ReproServiceView(ServiceView):
@ -106,8 +119,7 @@ def setup(helper, old_version=None):
], is_external=True, enable=enable, disable=disable)
helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
menu = main_menu.get('apps')
helper.call('post', menu.promote_item, 'repro:index')
helper.call('post', app.enable)
def add_shortcut():
@ -121,16 +133,14 @@ def enable():
"""Enable the module."""
actions.superuser_run('service', ['enable', managed_services[0]])
add_shortcut()
menu = main_menu.get('apps')
menu.promote_item('repro:index')
app.enable()
def disable():
"""Disable the module."""
actions.superuser_run('service', ['disable', managed_services[0]])
frontpage.remove_shortcut('repro')
menu = main_menu.get('apps')
menu.demote_item('repro:index')
app.disable()
def diagnose():

View File

@ -20,12 +20,12 @@ FreedomBox app to configure reStore.
from django.utils.translation import ugettext_lazy as _
from plinth import cfg
from plinth import app as app_module
from plinth import cfg, menu
from plinth import service as service_module
from plinth.menu import main_menu
from plinth.utils import format_lazy
from .manifest import clients
from .manifest import clients
version = 1
@ -45,7 +45,6 @@ description = [
'served from, the data can be stored on an unhosted storage '
'server of user\'s choice. With reStore, your {box_name} becomes '
'your unhosted storage server.'), box_name=_(cfg.box_name)),
_('You can create and edit accounts in the '
'<a href=\'/restore/\'>reStore web-interface</a>.')
]
@ -56,18 +55,32 @@ reserved_usernames = ['node-restore']
service = None
app = None
class RestoreApp(app_module.App):
"""FreedomBox app for Restore."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-restore', name, short_description,
'fa-hdd-o', 'restore:index',
parent_url_name='apps')
self.add(menu_item)
def init():
"""Initialize the reStore module."""
menu = main_menu.get('apps')
menu.add_urlname(name, 'fa-hdd-o', 'restore:index', short_description)
global app
app = RestoreApp()
global service
setup_helper = globals()['setup_helper']
if setup_helper.get_state() != 'needs-setup':
service = service_module.Service(
managed_services[0], name, ports=['http', 'https'],
is_external=False)
service = service_module.Service(managed_services[0], name,
ports=['http',
'https'], is_external=False)
def setup(helper, old_version=None):
@ -75,6 +88,6 @@ def setup(helper, old_version=None):
helper.install(managed_packages)
global service
if service is None:
service = service_module.Service(
managed_services[0], name, ports=['http', 'https'],
is_external=False)
service = service_module.Service(managed_services[0], name,
ports=['http',
'https'], is_external=False)

View File

@ -20,9 +20,10 @@ FreedomBox app to configure Roundcube.
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import frontpage, menu
from plinth import service as service_module
from plinth import action_utils, actions, frontpage
from plinth.menu import main_menu
from .manifest import backup, clients
@ -60,11 +61,25 @@ service = None
manual_page = 'Roundcube'
app = None
class RoundcubeApp(app_module.App):
"""FreedomBox app for Roundcube."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-roundcube', name, short_description,
'roundcube', 'roundcube:index',
parent_url_name='apps')
self.add(menu_item)
def init():
"""Intialize the module."""
menu = main_menu.get('apps')
menu.add_urlname(name, 'roundcube', 'roundcube:index', short_description)
global app
app = RoundcubeApp()
global service
setup_helper = globals()['setup_helper']
@ -75,7 +90,7 @@ def init():
if is_enabled():
add_shortcut()
menu.promote_item('roundcube:index')
app.set_enabled(True)
def setup(helper, old_version=None):
@ -89,8 +104,7 @@ def setup(helper, old_version=None):
service = service_module.Service(
'roundcube', name, ports=['http', 'https'], is_external=True,
is_enabled=is_enabled, enable=enable, disable=disable)
menu = main_menu.get('apps')
helper.call('post', menu.promote_item, 'roundcube:index')
helper.call('post', app.enable)
def add_shortcut():
@ -108,16 +122,14 @@ def enable():
"""Enable the module."""
actions.superuser_run('roundcube', ['enable'])
add_shortcut()
menu = main_menu.get('apps')
menu.promote_item('roundcube:index')
app.enable()
def disable():
"""Enable the module."""
actions.superuser_run('roundcube', ['disable'])
frontpage.remove_shortcut('roundcube')
menu = main_menu.get('apps')
menu.demote_item('roundcube:index')
app.disable()
def diagnose():

View File

@ -20,9 +20,10 @@ FreedomBox app to configure Searx.
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import frontpage, menu
from plinth import service as service_module
from plinth import action_utils, actions, frontpage
from plinth.menu import main_menu
from plinth.modules.users import register_group
from .manifest import backup, clients
@ -52,11 +53,24 @@ service = None
manual_page = 'Searx'
app = None
class SearxApp(app_module.App):
"""FreedomBox app for Searx."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-searx', name, short_description, 'searx',
'searx:index', parent_url_name='apps')
self.add(menu_item)
def init():
"""Intialize the module."""
menu = main_menu.get('apps')
menu.add_urlname(name, 'searx', 'searx:index', short_description)
global app
app = SearxApp()
register_group(group)
global service
@ -69,7 +83,7 @@ def init():
if is_enabled():
add_shortcut()
menu.promote_item('searx:index')
app.set_enabled(True)
def setup(helper, old_version=None):
@ -86,8 +100,7 @@ def setup(helper, old_version=None):
], is_external=True, is_enabled=is_enabled, enable=enable,
disable=disable)
helper.call('post', add_shortcut)
menu = main_menu.get('apps')
helper.call('post', menu.promote_item, 'searx:index')
helper.call('post', app.enable)
def add_shortcut():
@ -113,16 +126,14 @@ def enable():
"""Enable the module."""
actions.superuser_run('searx', ['enable'])
add_shortcut()
menu = main_menu.get('apps')
menu.promote_item('searx:index')
app.enable()
def disable():
"""Disable the module."""
actions.superuser_run('searx', ['disable'])
frontpage.remove_shortcut('searx')
menu = main_menu.get('apps')
menu.demote_item('searx:index')
app.disable()
def diagnose():

View File

@ -21,7 +21,8 @@ FreedomBox app for security configuration.
from django.utils.translation import ugettext_lazy as _
from plinth import actions
from plinth.menu import main_menu
from plinth import app as app_module
from plinth import menu
from .manifest import backup
@ -43,11 +44,25 @@ ACCESS_CONF_SNIPPET = '-:ALL EXCEPT root fbx plinth (admin) (sudo):ALL'
OLD_ACCESS_CONF_SNIPPET = '-:ALL EXCEPT root fbx (admin) (sudo):ALL'
ACCESS_CONF_SNIPPETS = [OLD_ACCESS_CONF_SNIPPET, ACCESS_CONF_SNIPPET]
app = None
class SecurityApp(app_module.App):
"""FreedomBox app for security."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-security', name, None, 'fa-lock',
'security:index', parent_url_name='system')
self.add(menu_item)
def init():
"""Initialize the module"""
menu = main_menu.get('system')
menu.add_urlname(name, 'fa-lock', 'security:index')
global app
app = SecurityApp()
app.set_enabled(True)
def setup(helper, old_version=None):

View File

@ -20,9 +20,10 @@ FreedomBox app to configure Shaarli.
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import frontpage, menu
from plinth import service as service_module
from plinth import action_utils, actions, frontpage
from plinth.menu import main_menu
from .manifest import clients
@ -48,12 +49,25 @@ service = None
manual_page = 'Shaarli'
app = None
class ShaarliApp(app_module.App):
"""FreedomBox app for Shaarli."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-shaarli', name, short_description,
'shaarli', 'shaarli:index',
parent_url_name='apps')
self.add(menu_item)
def init():
"""Initialize the module."""
menu = main_menu.get('apps')
menu.add_urlname(name, 'fa-bookmark', 'shaarli:index',
short_description)
global app
app = ShaarliApp()
global service
setup_helper = globals()['setup_helper']
@ -64,7 +78,7 @@ def init():
if is_enabled():
add_shortcut()
menu.promote_item('shaarli:index')
app.set_enabled(True)
def setup(helper, old_version=None):
@ -77,8 +91,7 @@ def setup(helper, old_version=None):
is_enabled=is_enabled, enable=enable, disable=disable)
helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
menu = main_menu.get('apps')
helper.call('post', menu.promote_item, 'shaarli:index')
helper.call('post', app.enable)
def add_shortcut():
@ -96,13 +109,11 @@ def enable():
"""Enable the module."""
actions.superuser_run('shaarli', ['enable'])
add_shortcut()
menu = main_menu.get('apps')
menu.promote_item('shaarli:index')
app.enable()
def disable():
"""Enable the module."""
actions.superuser_run('shaarli', ['disable'])
frontpage.remove_shortcut('shaarli')
menu = main_menu.get('apps')
menu.demote_item('shaarli:index')
app.disable()

View File

@ -21,9 +21,10 @@ FreedomBox app to configure Shadowsocks.
from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import cfg, frontpage, menu
from plinth import service as service_module
from plinth import action_utils, actions, cfg, frontpage
from plinth.menu import main_menu
from plinth.utils import format_lazy
from .manifest import backup
@ -56,12 +57,25 @@ description = [
manual_page = 'Shadowsocks'
app = None
class ShadowsocksApp(app_module.App):
"""FreedomBox app for Shadowsocks."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-shadowsocks', name, short_description,
'shadowsocks', 'shadowsocks:index',
parent_url_name='apps')
self.add(menu_item)
def init():
"""Intialize the module."""
menu = main_menu.get('apps')
menu.add_urlname(name, 'shadowsocks', 'shadowsocks:index',
short_description)
global app
app = ShadowsocksApp()
global service
setup_helper = globals()['setup_helper']
@ -73,7 +87,7 @@ def init():
if service.is_enabled():
add_shortcut()
menu.promote_item('shadowsocks:index')
app.set_enabled(True)
def setup(helper, old_version=None):
@ -87,8 +101,7 @@ def setup(helper, old_version=None):
], is_external=False, is_enabled=is_enabled, is_running=is_running,
enable=enable, disable=disable)
menu = main_menu.get('apps')
helper.call('post', menu.promote_item, 'shadowsocks:index')
helper.call('post', app.enable)
def add_shortcut():
@ -113,16 +126,14 @@ def enable():
"""Enable service."""
actions.superuser_run('service', ['enable', managed_services[0]])
add_shortcut()
menu = main_menu.get('apps')
menu.promote_item('shadowsocks:index')
app.enable()
def disable():
"""Disable service."""
actions.superuser_run('service', ['disable', managed_services[0]])
frontpage.remove_shortcut('shadowsocks')
menu = main_menu.get('apps')
menu.demote_item('shadowsocks:index')
app.disable()
def diagnose():

View File

@ -22,8 +22,9 @@ import json
from django.utils.translation import ugettext_lazy as _
from plinth import actions, cfg
from plinth.menu import main_menu
from plinth import actions
from plinth import app as app_module
from plinth import cfg, menu
from plinth.utils import format_lazy
from .manifest import backup
@ -35,16 +36,29 @@ name = _('Sharing')
description = [
format_lazy(
_('Sharing allows you to share files and folders on your {box_name} '
'over the web with chosen groups of users.'), box_name=_(
cfg.box_name))
'over the web with chosen groups of users.'),
box_name=_(cfg.box_name))
]
app = None
class SharingApp(app_module.App):
"""FreedomBox app for sharing files."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-sharing', name, None, 'sharing',
'sharing:index', parent_url_name='apps')
self.add(menu_item)
def init():
"""Initialize the module."""
menu = main_menu.get('apps')
menu.add_urlname(name, 'sharing', 'sharing:index')
menu.promote_item('sharing:index')
global app
app = SharingApp()
app.set_enabled(True)
def list_shares():

View File

@ -24,7 +24,8 @@ import augeas
from django.utils.translation import ugettext_lazy as _
from plinth import actions
from plinth.menu import main_menu
from plinth import app as app_module
from plinth import menu
from plinth.modules import storage
from .manifest import backup
@ -57,11 +58,29 @@ DEFAULT_FILE = '/etc/default/snapper'
fs_types_supported = ['btrfs']
app = None
class SnapshotApp(app_module.App):
"""FreedomBox app for snapshots."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-snapshot', name, None, 'fa-film',
'snapshot:index', parent_url_name='system')
self.add(menu_item)
def init():
"""Initialize the module."""
menu = main_menu.get('system')
menu.add_urlname(name, 'fa-film', 'snapshot:index')
global app
app = SnapshotApp()
global service
setup_helper = globals()['setup_helper']
if setup_helper.get_state() != 'needs-setup':
app.set_enabled(True) # XXX: Perform better checks
def is_supported():
@ -77,12 +96,13 @@ def setup(helper, old_version=None):
helper.call('post', actions.superuser_run, 'snapshot',
['setup', '--old-version',
str(old_version)])
helper.call('post', app.enable)
def load_augeas():
"""Initialize Augeas."""
aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD +
augeas.Augeas.NO_MODL_AUTOLOAD)
aug = augeas.Augeas(
flags=augeas.Augeas.NO_LOAD + augeas.Augeas.NO_MODL_AUTOLOAD)
# shell-script config file lens
aug.set('/augeas/load/Shellvars/lens', 'Shellvars.lns')

View File

@ -21,8 +21,9 @@ FreedomBox app for OpenSSH server.
from django.utils.translation import ugettext_lazy as _
from plinth import actions
from plinth import app as app_module
from plinth import cfg, menu
from plinth import service as service_module
from plinth.menu import main_menu
from plinth.views import ServiceView
from .manifest import backup
@ -48,15 +49,29 @@ service = None
port_forwarding_info = [('TCP', 22)]
app = None
class SSHApp(app_module.App):
"""FreedomBox app for SSH."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-ssh', name, None, 'fa-terminal',
'ssh:index', parent_url_name='system')
self.add(menu_item)
def init():
"""Intialize the ssh module."""
menu = main_menu.get('system')
menu.add_urlname(name, 'fa-terminal', 'ssh:index')
global app
app = SSHApp()
app.set_enabled(True)
global service
service = service_module.Service(
managed_services[0], name, ports=['ssh'], is_external=True)
service = service_module.Service(managed_services[0], name, ports=['ssh'],
is_external=True)
def setup(helper, old_version=None):

View File

@ -23,11 +23,12 @@ import subprocess
import psutil
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions, cfg
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import cfg, menu
from plinth import service as service_module
from plinth import utils
from plinth.errors import PlinthError
from plinth.menu import main_menu
from plinth.utils import format_lazy, import_from_gi
version = 3
@ -54,11 +55,25 @@ manual_page = 'Storage'
is_essential = True
app = None
class StorageApp(app_module.App):
"""FreedomBox app for storage."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-storage', name, None, 'fa-hdd-o',
'storage:index', parent_url_name='system')
self.add(menu_item)
def init():
"""Intialize the module."""
menu = main_menu.get('system')
menu.add_urlname(name, 'fa-hdd-o', 'storage:index')
global app
app = StorageApp()
app.set_enabled(True)
def get_disks():
@ -267,11 +282,13 @@ def is_enabled():
def enable():
"""Enable the module."""
actions.superuser_run('udiskie', ['enable'])
app.enable()
def disable():
"""Disable the module."""
actions.superuser_run('udiskie', ['disable'])
app.disable()
def setup(helper, old_version=None):

View File

@ -20,9 +20,10 @@ FreedomBox app to configure Syncthing.
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import cfg, frontpage, menu
from plinth import service as service_module
from plinth import action_utils, actions, cfg, frontpage
from plinth.menu import main_menu
from plinth.modules.users import register_group
from plinth.utils import format_lazy
@ -64,11 +65,25 @@ service = None
manual_page = 'Syncthing'
app = None
class SyncthingApp(app_module.App):
"""FreedomBox app for Syncthing."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-syncthing', name, short_description,
'syncthing', 'syncthing:index',
parent_url_name='apps')
self.add(menu_item)
def init():
"""Intialize the module."""
menu = main_menu.get('apps')
menu.add_urlname(name, 'syncthing', 'syncthing:index', short_description)
global app
app = SyncthingApp()
register_group(group)
global service
@ -82,7 +97,7 @@ def init():
if is_enabled():
add_shortcut()
menu.promote_item('syncthing:index')
app.set_enabled(True)
def setup(helper, old_version=None):
@ -98,8 +113,7 @@ def setup(helper, old_version=None):
is_running=is_running)
helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
menu = main_menu.get('apps')
helper.call('post', menu.promote_item, 'syncthing:index')
helper.call('post', app.enable)
def add_shortcut():
@ -124,16 +138,14 @@ def enable():
"""Enable the module."""
actions.superuser_run('syncthing', ['enable'])
add_shortcut()
menu = main_menu.get('apps')
menu.promote_item('syncthing:index')
app.enable()
def disable():
"""Enable the module."""
actions.superuser_run('syncthing', ['disable'])
frontpage.remove_shortcut('syncthing')
menu = main_menu.get('apps')
menu.demote_item('syncthing:index')
app.disable()
def diagnose():

View File

@ -23,9 +23,10 @@ import os
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions, cfg, frontpage
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import cfg, frontpage, menu
from plinth import service as service_module
from plinth.menu import main_menu
from plinth.utils import format_lazy
from .errors import TahoeConfigurationError
@ -57,6 +58,20 @@ introducers_file = os.path.join(
introducer_furl_file = os.path.join(
tahoe_home, '{0}/private/{0}.furl'.format(introducer_name))
app = None
class TahoeApp(app_module.App):
"""FreedomBox app for Tahoe LAFS."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-tahoe', name, short_description,
'tahoe-lafs', 'tahoe:index',
parent_url_name='apps')
self.add(menu_item)
def is_setup():
"""Check whether Tahoe-LAFS is setup"""
@ -88,8 +103,8 @@ description = [
def init():
"""Intialize the module."""
menu = main_menu.get('apps')
menu.add_urlname(name, 'tahoe-lafs', 'tahoe:index', short_description)
global app
app = TahoeApp()
global service
setup_helper = globals()['setup_helper']
@ -102,7 +117,7 @@ def init():
if is_enabled():
add_shortcut()
menu.promote_item('tahoe:index')
app.set_enabled(True)
def setup(helper, old_version=None):
@ -130,8 +145,7 @@ def post_setup(configured_domain_name):
is_running=is_running)
service.notify_enabled(None, True)
add_shortcut()
menu = main_menu.get('apps')
menu.promote_item('tahoe:index')
app.enable()
def add_shortcut():
@ -158,16 +172,14 @@ def enable():
"""Enable the module."""
actions.superuser_run('tahoe-lafs', ['enable'])
add_shortcut()
menu = main_menu.get('apps')
menu.promote_item('tahoe:index')
app.enable()
def disable():
"""Enable the module."""
actions.superuser_run('tahoe-lafs', ['disable'])
frontpage.remove_shortcut('tahoe-lafs')
menu = main_menu.get('apps')
menu.demote_item('tahoe:index')
app.disable()
def diagnose():

View File

@ -22,9 +22,10 @@ import json
from django.utils.translation import ugettext_lazy as _
from plinth import service as service_module
from plinth import action_utils, actions
from plinth.menu import main_menu
from plinth import app as app_module
from plinth import menu
from plinth import service as service_module
from plinth.modules.names import SERVICES
from plinth.signals import domain_added, domain_removed
@ -61,18 +62,31 @@ bridge_service = None
manual_page = 'Tor'
app = None
class TorApp(app_module.App):
"""FreedomBox app for Tor."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-tor', name, short_description, 'tor',
'tor:index', parent_url_name='apps')
self.add(menu_item)
def init():
"""Initialize the module."""
menu = main_menu.get('apps')
menu.add_urlname(name, 'tor', 'tor:index', short_description)
global app
app = TorApp()
setup_helper = globals()['setup_helper']
needs_setup = setup_helper.get_state() == 'needs-setup'
if not needs_setup:
if utils.is_enabled():
menu.promote_item('tor:index')
app.set_enabled(True)
global socks_service
socks_service = service_module.Service(
@ -137,8 +151,7 @@ def setup(helper, old_version=None):
helper.call('post', bridge_service.notify_enabled, None, True)
helper.call('post', update_hidden_service_domain)
menu = main_menu.get('apps')
helper.call('post', menu.promote_item, 'tor:index')
helper.call('post', app.enable)
def enable():
@ -148,8 +161,7 @@ def enable():
elsewhere.
"""
menu = main_menu.get('apps')
menu.promote_item('tor:index')
app.enable()
def disable():
@ -159,8 +171,7 @@ def disable():
elsewhere.
"""
menu = main_menu.get('apps')
menu.demote_item('tor:index')
app.disable()
def update_hidden_service_domain(status=None):

View File

@ -22,9 +22,10 @@ import json
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import frontpage, menu
from plinth import service as service_module
from plinth import action_utils, actions, frontpage
from plinth.menu import main_menu
from plinth.modules.users import register_group
from .manifest import backup, clients
@ -56,12 +57,25 @@ service = None
manual_page = 'Transmission'
app = None
class TransmissionApp(app_module.App):
"""FreedomBox app for Transmission."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-transmission', name, short_description,
'transmission', 'transmission:index',
parent_url_name='apps')
self.add(menu_item)
def init():
"""Intialize the Transmission module."""
menu = main_menu.get('apps')
menu.add_urlname(name, 'transmission', 'transmission:index',
short_description)
"""Initialize the Transmission module."""
global app
app = TransmissionApp()
register_group(group)
global service
@ -74,7 +88,7 @@ def init():
if is_enabled():
add_shortcut()
menu.promote_item('transmission:index')
app.set_enabled(True)
def setup(helper, old_version=None):
@ -98,8 +112,7 @@ def setup(helper, old_version=None):
disable=disable)
helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
menu = main_menu.get('apps')
helper.call('post', menu.promote_item, 'transmission:index')
helper.call('post', app.enable)
def add_shortcut():
@ -118,16 +131,14 @@ def enable():
"""Enable the module."""
actions.superuser_run('transmission', ['enable'])
add_shortcut()
menu = main_menu.get('apps')
menu.promote_item('transmission:index')
app.enable()
def disable():
"""Enable the module."""
actions.superuser_run('transmission', ['disable'])
frontpage.remove_shortcut('transmission')
menu = main_menu.get('apps')
menu.demote_item('transmission:index')
app.disable()
def diagnose():

View File

@ -21,9 +21,10 @@ FreedomBox app to configure Tiny Tiny RSS.
from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions, cfg, frontpage
from plinth import action_utils, actions
from plinth import app as app_module
from plinth import cfg, frontpage, menu
from plinth import service as service_module
from plinth.menu import main_menu
from plinth.modules.users import register_group
from plinth.utils import Version, format_lazy
@ -63,11 +64,24 @@ service = None
manual_page = 'TinyTinyRSS'
app = None
class TTRSSApp(app_module.App):
"""FreedomBox app for TT-RSS."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-ttrss', name, short_description, 'ttrss',
'ttrss:index', parent_url_name='apps')
self.add(menu_item)
def init():
"""Intialize the module."""
menu = main_menu.get('apps')
menu.add_urlname(name, 'ttrss', 'ttrss:index', short_description)
global app
app = TTRSSApp()
register_group(group)
global service
@ -80,7 +94,7 @@ def init():
if is_enabled():
add_shortcut()
menu.promote_item('ttrss:index')
app.set_enabled(True)
def setup(helper, old_version=None):
@ -97,8 +111,7 @@ def setup(helper, old_version=None):
disable=disable)
helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut)
menu = main_menu.get('apps')
helper.call('post', menu.promote_item, 'ttrss:index')
helper.call('post', app.enable)
def force_upgrade(helper, packages):
@ -133,16 +146,14 @@ def enable():
"""Enable the module."""
actions.superuser_run('ttrss', ['enable'])
add_shortcut()
menu = main_menu.get('apps')
menu.promote_item('ttrss:index')
app.enable()
def disable():
"""Enable the module."""
actions.superuser_run('ttrss', ['disable'])
frontpage.remove_shortcut('ttrss')
menu = main_menu.get('apps')
menu.demote_item('ttrss:index')
app.disable()
def diagnose():

View File

@ -20,9 +20,10 @@ FreedomBox app for upgrades.
from django.utils.translation import ugettext_lazy as _
from plinth import service as service_module
from plinth import actions
from plinth.menu import main_menu
from plinth import app as app_module
from plinth import menu
from plinth import service as service_module
from .manifest import backup
@ -42,11 +43,26 @@ service = None
manual_page = 'Upgrades'
app = None
class UpgradesApp(app_module.App):
"""FreedomBox app for software upgrades."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-upgrades', name, None, 'fa-refresh',
'upgrades:index', parent_url_name='system')
self.add(menu_item)
def init():
"""Initialize the module."""
menu = main_menu.get('system')
menu.add_urlname(name, 'fa-refresh', 'upgrades:index')
global app
app = UpgradesApp()
app.set_enabled(True)
global service
service = service_module.Service('auto-upgrades', name, is_external=False,
is_enabled=is_enabled, enable=enable,

View File

@ -23,7 +23,8 @@ import subprocess
from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions
from plinth.menu import main_menu
from plinth import app as app_module
from plinth import menu
version = 2
@ -47,11 +48,25 @@ name = _('Users and Groups')
# All FreedomBox user groups
groups = dict()
app = None
class UsersApp(app_module.App):
"""FreedomBox app for users and groups management."""
def __init__(self):
"""Create components for the app."""
super().__init__()
menu_item = menu.Menu('menu-users', name, None, 'fa-users',
'users:index', parent_url_name='system')
self.add(menu_item)
def init():
"""Intialize the user module."""
menu = main_menu.get('system')
menu.add_urlname(name, 'fa-users', 'users:index')
global app
app = UsersApp()
app.set_enabled(True)
def setup(helper, old_version=None):

View File

@ -34,7 +34,7 @@
<div class="row">
<div class="card-list card-list-primary">
{% for item in submenu.sorted_items %}
{% if not show_secondary or not item.secondary %}
{% if not show_disabled or item.is_enabled %}
<div class="card thumbnail">
<a href="{{ item.url }}" class="nav-link">
<div class="card-title">{{ item.name }}</div>
@ -45,7 +45,7 @@
<img src="{% static 'theme/icons/' %}{{ item.icon }}.svg"/>
{% endif %}
</div>
<div class="card-description">{{ item.short_description}}</div>
<div class="card-description">{{ item.short_description|default:'' }}</div>
</a>
</div>
{% endif %}
@ -54,13 +54,13 @@
</div>
</div>
{% if show_secondary %}
{% if show_disabled %}
<div class="container card-container">
<div class="card-section-title text-center">{% trans "Disabled" %}</div>
<div class="row">
<div class="card-list card-list-secondary">
<div class="card-list card-list-disabled">
{% for item in submenu.sorted_items %}
{% if item.secondary %}
{% if not item.is_enabled %}
<div class="card thumbnail">
<a href="{{ item.url }}" class="nav-link">
<div class="card-title">{{ item.name }}</div>
@ -71,7 +71,7 @@
<img src="{% static 'theme/icons/' %}{{ item.icon }}.svg"/>
{% endif %}
</div>
<div class="card-description">{{ item.short_description}}</div>
<div class="card-description">{{ item.short_description|default:'' }}</div>
</a>
</div>
{% endif %}

View File

@ -87,7 +87,7 @@
{% endif %}
</div>
<div class="card-description">
{{ shortcut.short_description }}
{{ shortcut.short_description|default:'' }}
</div>
</a>
</div>

View File

@ -36,7 +36,7 @@
{% block content %}
<h2>{% trans "Installation" %}: {{ setup_helper.module.short_description }} ({{ setup_helper.module.name }}) </h2>
<h2>{% trans "Installation" %}: {{ setup_helper.module.short_description|default:'' }} ({{ setup_helper.module.name }}) </h2>
{% for paragraph in setup_helper.module.description %}
<p>{{ paragraph|safe }}</p>

163
plinth/tests/test_app.py Normal file
View File

@ -0,0 +1,163 @@
#
# 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 App, base class for all applications.
"""
import collections
import pytest
from plinth.app import App, Component, FollowerComponent, LeaderComponent
class LeaderTest(FollowerComponent):
"""Test class for using LeaderComponent in tests."""
is_leader = True
@pytest.fixture(name='app_with_components')
def fixture_app_with_components():
"""Setup an app with some components."""
app = App()
app.add(FollowerComponent('test-follower-1'))
app.add(FollowerComponent('test-follower-2'))
app.add(LeaderTest('test-leader-1'))
app.add(LeaderTest('test-leader-2'))
return app
def test_app_instantiation():
"""Test that App is instantiated properly."""
app = App()
assert isinstance(app.components, collections.OrderedDict)
assert not app.components
def test_app_add():
"""Test adding a components to an App."""
app = App()
component = Component('test-component')
return_value = app.add(component)
assert len(app.components) == 1
assert app.components['test-component'] == component
assert return_value == app
def test_app_enable(app_with_components):
"""Test that enabling an app enables components."""
app_with_components.disable()
app_with_components.enable()
for component in app_with_components.components.values():
assert component.is_enabled()
def test_app_disable(app_with_components):
"""Test that disabling an app disables components."""
app_with_components.enable()
app_with_components.disable()
for component in app_with_components.components.values():
assert not component.is_enabled()
def test_app_is_enabled(app_with_components):
"""Test checking for app enabled."""
app = app_with_components
app.disable()
# Disabling the components disables that app
assert not app.is_enabled()
# Enabling followers will not enable the app
app.components['test-follower-1'].enable()
assert not app.is_enabled()
app.components['test-follower-2'].enable()
assert not app.is_enabled()
# Enabling both leaders will enable the app
app.components['test-leader-1'].enable()
assert not app.is_enabled()
app.components['test-leader-2'].enable()
assert app.is_enabled()
# Disabling followers has no effect
app.components['test-follower-1'].disable()
assert app.is_enabled()
def test_app_set_enabled(app_with_components):
"""Test that setting enabled effects only followers."""
app = app_with_components
app.disable()
app.set_enabled(True)
assert app.components['test-follower-1'].is_enabled()
assert not app.components['test-leader-1'].is_enabled()
app.enable()
app.set_enabled(False)
assert not app.components['test-follower-1'].is_enabled()
assert app.components['test-leader-1'].is_enabled()
def test_component_initialization():
"""Test that component is initialized properly."""
with pytest.raises(ValueError):
Component(None)
component = Component('test-component')
assert component.component_id == 'test-component'
assert not component.is_leader
def test_follower_component_initialization():
"""Test that follower component is initialized properly."""
component = FollowerComponent('test-follower-1')
assert not component.is_enabled()
component = FollowerComponent('test-follower-2', False)
assert not component.is_enabled()
component = FollowerComponent('test-follower-3', True)
assert component.is_enabled()
def test_follower_component_enable():
"""Test enabling a follower component."""
component = FollowerComponent('test-follower-1', False)
component.enable()
assert component.is_enabled()
def test_follower_component_disable():
"""Test disabling a follower component."""
component = FollowerComponent('test-follower-1', True)
component.disable()
assert not component.is_enabled()
def test_leader_component_initialization():
"""Test that leader component is initialized properly."""
component = LeaderComponent('test-leader-1')
assert component.is_leader
def test_leader_component_is_enabled():
"""Test getting enabled state is not implemented in leader component."""
component = LeaderComponent('test-leader-1')
with pytest.raises(NotImplementedError):
assert component.is_enabled()

View File

@ -21,10 +21,17 @@ Test module for custom context processors.
from unittest.mock import MagicMock, Mock
import pytest
from django.http import HttpRequest
from plinth import cfg
from plinth import context_processors as cp
from plinth import menu as menu_module
@pytest.fixture(name='menu', autouse=True)
def fixture_menu():
"""Initialized menu module."""
menu_module.init()
def test_common():

View File

@ -36,17 +36,25 @@ def build_menu(size=5):
"""Build a menu with the specified number of items."""
random.seed()
menu = Menu()
menu = Menu('menu-index', url_name='index')
for index in range(1, size + 1):
args = [
'Name' + str(index), 'Icon' + str(index), 'test',
'ShortDescription' + str(index),
random.randint(0, 1000)
]
kwargs = {'url_kwargs': {'a': index, 'b': index, 'c': index}}
kwargs = {
'component_id': 'menu-test-' + str(index),
'name': 'Name' + str(index),
'short_description': 'ShortDescription' + str(index),
'icon': 'Icon' + str(index),
'url_name': 'test',
'url_kwargs': {
'a': index,
'b': index,
'c': index
},
'parent_url_name': 'index',
'order': random.randint(0, 1000),
}
menu.add_urlname(*args, **kwargs)
Menu(**kwargs)
return menu
@ -74,9 +82,15 @@ def test_init():
def test_menu_creation_without_arguments():
"""Verify the Menu state without initialization parameters."""
menu = Menu()
assert menu.icon == ''
assert menu.url == '#'
with pytest.raises(ValueError):
Menu('menu-test')
menu = Menu('menu-index', url_name='index')
assert menu.component_id == 'menu-index'
assert menu.name is None
assert menu.short_description is None
assert menu.icon is None
assert menu.url == '/'
assert menu.order == 50
assert not menu.items
@ -86,11 +100,17 @@ def test_menu_creation_with_arguments():
expected_name = 'Name'
expected_short_description = 'ShortDescription'
expected_icon = 'Icon'
expected_url = '/aaa/bbb/ccc/'
url_name = 'test'
url_kwargs = {'a': 1, 'b': 2, 'c': 3}
expected_url = reverse(url_name, kwargs=url_kwargs)
expected_order = 42
menu = Menu(expected_name, expected_short_description, expected_icon,
expected_url, expected_order)
parent_menu = Menu('menu-index', url_name='index')
menu = Menu('menu-test', expected_name, expected_short_description,
expected_icon, url_name, url_kwargs=url_kwargs,
parent_url_name='index', order=expected_order)
assert len(parent_menu.items) == 1
assert parent_menu.items[0] == menu
assert expected_name == menu.name
assert expected_short_description == menu.short_description
assert expected_icon == menu.icon
@ -105,11 +125,11 @@ def test_get():
expected_short_description = 'ShortDescription2'
expected_icon = 'Icon2'
expected_url = 'index'
reversed_url = reverse(expected_url)
url_name = 'index'
reversed_url = reverse(url_name)
expected_order = 2
menu = Menu()
menu.add_urlname(expected_name, expected_icon, expected_url,
expected_short_description, expected_order)
menu = Menu('menu-test', expected_name, expected_short_description,
expected_icon, url_name, order=expected_order)
actual_item = menu.get(expected_url)
assert actual_item is not None
@ -126,11 +146,10 @@ def test_get_with_item_not_found():
expected_name = 'Name3'
expected_short_description = 'ShortDescription3'
expected_icon = 'Icon3'
expected_url = 'index'
url_name = 'index'
expected_order = 3
menu = Menu()
menu.add_urlname(expected_name, expected_icon, expected_url,
expected_short_description, expected_order)
menu = Menu('menu-test', expected_name, expected_short_description,
expected_icon, url_name, order=expected_order)
with pytest.raises(KeyError):
menu.get('apps')
@ -146,7 +165,7 @@ def test_sort_items():
# Verify that the order of every item is equal to or greater
# than the order of the item preceding it and if the order is
# the same, the labels are considered.
# the same, the names are considered.
items = menu.sorted_items()
for index in range(1, size):
assert items[index].order >= items[index - 1].order
@ -154,28 +173,6 @@ def test_sort_items():
assert items[index].name >= items[index - 1].name
def test_add_urlname():
"""Verify that a named URL can be added to a menu correctly."""
expected_name = 'Name4'
expected_short_description = 'Description4'
expected_icon = 'Icon4'
expected_url = 'index'
reversed_url = reverse(expected_url)
expected_order = 4
menu = Menu()
actual_item = menu.add_urlname(expected_name, expected_icon, expected_url,
expected_short_description, expected_order)
assert len(menu.items) == 1
assert actual_item is not None
assert actual_item == menu.items[0]
assert expected_name == actual_item.name
assert expected_icon == actual_item.icon
assert reversed_url == actual_item.url
assert expected_order == actual_item.order
assert not actual_item.items
def test_active_item():
"""Verify that an active menu item can be correctly retrieved."""
menu = build_menu()

View File

@ -72,7 +72,7 @@ class AppsIndexView(TemplateView):
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['show_secondary'] = True
context['show_disabled'] = True
return context

View File

@ -299,12 +299,12 @@ footer {
}
/* Disabled app icons */
.apps-page .card-list-secondary .card-icon img {
.apps-page .card-list-disabled .card-icon img {
transition: filter 0.3s;
filter: grayscale(1) opacity(0.5);
}
.apps-page .card-list-secondary .card:hover .card-icon img {
.apps-page .card-list-disabled .card:hover .card-icon img {
filter: grayscale(1) opacity(1);
}
@ -385,7 +385,7 @@ a.menu_link_active {
}
/* Disabled apps - grey glow */
.card-list-secondary .card-icon {
.card-list-disabled .card-icon {
background: radial-gradient(farthest-side at bottom, #f0f0f0, white);
background-position: 50% 100%;
background-repeat: no-repeat;