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 from django.utils.translation import ugettext as _, ugettext_noop
import re import re
from plinth import cfg from plinth import cfg, menu
from plinth.menu import main_menu
from plinth.utils import is_user_admin 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] active_menu_urls = [request.path[:index + 1] for index in slash_indices]
return { return {
'cfg': cfg, 'cfg': cfg,
'submenu': main_menu.active_item(request), 'submenu': menu.main_menu.active_item(request),
'active_menu_urls': active_menu_urls, 'active_menu_urls': active_menu_urls,
'box_name': _(cfg.box_name), 'box_name': _(cfg.box_name),
'user_is_admin': is_user_admin(request, True) 'user_is_admin': is_user_admin(request, True)

View File

@ -17,106 +17,88 @@
from django.urls import reverse, reverse_lazy from django.urls import reverse, reverse_lazy
from plinth.utils import format_lazy from plinth import app
class Menu(object): class Menu(app.FollowerComponent):
"""One menu item.""" """Component to manage a single menu item."""
def __init__(self, name="", short_description="", icon="", url="#", _all_menus = {}
order=50):
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. """Initialize a new menu item with basic properties.
icon is the icon to be displayed for the menu item. name is the label of the menu item.
Choose from the Fork Awesome set:
https://forkawesome.github.io/Fork-Awesome/icons/
url is the url location that will be activated when the menu short_description is an optional description shown on the menu item.
item is selected.
order is the numerical rank of this item within the menu. icon is the icon to be displayed for the menu item. Choose from the
Lower order items appear closest to the top/left of the menu. Fork Awesome set: https://forkawesome.github.io/Fork-Awesome/icons/
By convention, we use the spectrum between 0 and 100 to rank
orders, but feel free to disregard that. If you need more url_name is the name of url location that will be activated when the
granularity, don't bother renumbering things. Feel free to menu item is selected. This is not optional. url_args and url_kwargs
use fractional orders. 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.name = name
self.short_description = short_description self.short_description = short_description
self.icon = icon self.icon = icon
self.url = url self.url = url
self.order = order 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 = [] 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.""" """Return a menu item with given URL name."""
url = reverse(urlname, args=url_args, kwargs=url_kwargs) url = reverse(urlname, args=url_args, kwargs=url_kwargs)
for item in self.items: return cls._all_menus[url]
if str(item.url) == url:
return item
raise KeyError('Menu item not found')
def sorted_items(self): def sorted_items(self):
"""Return menu items in sorted order according to current locale.""" """Return menu items in sorted order according to current locale."""
return sorted(self.items, key=lambda x: (x.order, x.name.lower())) 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): def active_item(self, request):
"""Return the first active item (e.g. submenu) that is found.""" """Return the first active item (e.g. submenu) that is found."""
for item in self.items: for item in self.items:
if request.path.startswith(str(item.url)): if request.path.startswith(str(item.url)):
return item return item
def promote_item(self, urlname, url_args=None, url_kwargs=None): return 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')
main_menu = Menu() main_menu = None
def init(): def init():
"""Create main menu and other essential menus.""" """Create main menu and other essential menus."""
main_menu.add_urlname('', 'fa-download', 'apps') global main_menu
main_menu.add_urlname('', 'fa-cog', 'system') 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 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 service as service_module
from plinth import actions, cfg
from plinth.menu import main_menu
from plinth.utils import format_lazy from plinth.utils import format_lazy
from plinth.views import ServiceView from plinth.views import ServiceView
@ -55,11 +56,25 @@ service = None
manual_page = 'ServiceDiscovery' 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(): def init():
"""Intialize the service discovery module.""" """Intialize the service discovery module."""
menu = main_menu.get('system') global app
menu.add_urlname(name, 'fa-compass', 'avahi:index') app = AvahiApp()
app.set_enabled(True)
global service # pylint: disable=W0603 global service # pylint: disable=W0603
service = service_module.Service(managed_services[0], name, ports=['mdns'], 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.text import get_valid_filename
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth import actions, cfg from plinth import actions
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 plinth.utils import format_lazy
from . import api from . import api
@ -46,24 +47,43 @@ manual_page = 'Backups'
MANIFESTS_FOLDER = '/var/lib/plinth/backups-manifests/' MANIFESTS_FOLDER = '/var/lib/plinth/backups-manifests/'
ROOT_REPOSITORY = '/var/lib/freedombox/borgbackup' ROOT_REPOSITORY = '/var/lib/freedombox/borgbackup'
ROOT_REPOSITORY_NAME = format_lazy(_('{box_name} storage'), ROOT_REPOSITORY_NAME = format_lazy(
box_name=cfg.box_name) _('{box_name} storage'), box_name=cfg.box_name)
ROOT_REPOSITORY_UUID = 'root' ROOT_REPOSITORY_UUID = 'root'
# session variable name that stores when a backup file should be deleted # session variable name that stores when a backup file should be deleted
SESSION_PATH_VARIABLE = 'fbx-backups-upload-path' 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(): def init():
"""Intialize the module.""" """Intialize the module."""
menu = main_menu.get('system') global app
menu.add_urlname(name, 'fa-files-o', 'backups:index') 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): def setup(helper, old_version=None):
"""Install and configure the module.""" """Install and configure the module."""
helper.install(managed_packages) helper.install(managed_packages)
helper.call('post', actions.superuser_run, 'backups', ['setup', '--path', helper.call('post', actions.superuser_run, 'backups',
ROOT_REPOSITORY]) ['setup', '--path', ROOT_REPOSITORY])
helper.call('post', app.enable)
def _backup_handler(packet, encryption_passphrase=None): 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.""" """Perform restore operation on packet."""
locations = {'directories': packet.directories, 'files': packet.files} locations = {'directories': packet.directories, 'files': packet.files}
locations_data = json.dumps(locations) locations_data = json.dumps(locations)
actions.superuser_run('backups', ['restore-exported-archive', '--path', actions.superuser_run('backups',
packet.path], ['restore-exported-archive', '--path', packet.path],
input=locations_data.encode()) input=locations_data.encode())
@ -111,8 +131,9 @@ def restore_archive_handler(packet, encryption_passphrase=None):
"""Perform restore operation on packet.""" """Perform restore operation on packet."""
locations = {'directories': packet.directories, 'files': packet.files} locations = {'directories': packet.directories, 'files': packet.files}
locations_data = json.dumps(locations) locations_data = json.dumps(locations)
arguments = ['restore-archive', '--path', packet.path, '--destination', arguments = [
'/'] 'restore-archive', '--path', packet.path, '--destination', '/'
]
if encryption_passphrase: if encryption_passphrase:
arguments += ['--encryption-passphrase', encryption_passphrase] arguments += ['--encryption-passphrase', encryption_passphrase]
actions.superuser_run('backups', arguments, input=locations_data.encode()) 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 django.utils.translation import ugettext_lazy as _
from plinth import actions from plinth import action_utils, actions
from plinth import action_utils from plinth import app as app_module
from plinth import cfg from plinth import cfg, menu
from plinth import service as service_module from plinth import service as service_module
from plinth.menu import main_menu
from plinth.utils import format_lazy from plinth.utils import format_lazy
from .manifest import backup 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(): def init():
"""Intialize the BIND module.""" """Intialize the BIND module."""
menu = main_menu.get('system') global app
menu.add_urlname(name, 'fa-globe-w', 'bind:index', short_description) app = BindApp()
global service global service
setup_helper = globals()['setup_helper'] setup_helper = globals()['setup_helper']
if setup_helper.get_state() != 'needs-setup': if setup_helper.get_state() != 'needs-setup':
service = service_module.Service(managed_services[0], name, service = service_module.Service(managed_services[0], name,
ports=['dns'], is_external=False) ports=['dns'], is_external=False)
app.set_enabled(True) # XXX: Perform better check
def setup(helper, old_version=None): def setup(helper, old_version=None):
@ -107,6 +121,7 @@ def setup(helper, old_version=None):
enable=enable, disable=disable) enable=enable, disable=disable)
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', actions.superuser_run, 'bind', ['setup']) helper.call('post', actions.superuser_run, 'bind', ['setup'])
helper.call('post', app.enable)
def force_upgrade(helper, _packages): def force_upgrade(helper, _packages):
@ -117,11 +132,13 @@ def force_upgrade(helper, _packages):
def enable(): def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('service', ['enable', managed_services[0]]) actions.superuser_run('service', ['enable', managed_services[0]])
app.enable()
def disable(): def disable():
"""Disable the module.""" """Disable the module."""
actions.superuser_run('service', ['disable', managed_services[0]]) actions.superuser_run('service', ['disable', managed_services[0]])
app.disable()
def diagnose(): def diagnose():

View File

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

View File

@ -25,7 +25,8 @@ import augeas
from django.utils.translation import ugettext_lazy from django.utils.translation import ugettext_lazy
from plinth import actions 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 import firewall
from plinth.modules.names import SERVICES from plinth.modules.names import SERVICES
from plinth.signals import domain_added from plinth.signals import domain_added
@ -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_APACHE_CONFIG = os.path.join(APACHE_CONF_ENABLED_DIR,
'freedombox.conf') '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(): def get_domainname():
"""Return the domainname""" """Return the domainname"""
@ -89,8 +104,9 @@ def get_home_page():
def init(): def init():
"""Initialize the module""" """Initialize the module"""
menu = main_menu.get('system') global app
menu.add_urlname(ugettext_lazy('Configure'), 'fa-cog', 'config:index') app = ConfigApp()
app.set_enabled(True)
# Register domain with Name Services module. # Register domain with Name Services module.
domainname = get_domainname() domainname = get_domainname()

View File

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

View File

@ -22,8 +22,9 @@ import subprocess
from django.utils.translation import ugettext_lazy as _ 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 import service as service_module
from plinth.menu import main_menu
from .manifest import backup from .manifest import backup
@ -46,11 +47,25 @@ manual_page = 'DateTime'
service = None 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(): def init():
"""Intialize the date/time module.""" """Intialize the date/time module."""
menu = main_menu.get('system') global app
menu.add_urlname(name, 'fa-clock-o', 'datetime:index') app = DateTimeApp()
app.set_enabled(True)
global service global service
setup_helper = globals()['setup_helper'] setup_helper = globals()['setup_helper']
@ -67,6 +82,7 @@ def setup(helper, old_version=None):
is_external=True) is_external=True)
service.enable() service.enable()
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', app.enable)
def diagnose(): 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 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 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 plinth.modules.users import register_group
from .manifest import backup, clients from .manifest import backup, clients
@ -55,11 +56,24 @@ clients = clients
manual_page = 'Deluge' 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(): def init():
"""Initialize the Deluge module.""" """Initialize the Deluge module."""
menu = main_menu.get('apps') global app
menu.add_urlname(name, 'deluge', 'deluge:index', short_description) app = DelugeApp()
register_group(group) register_group(group)
global service global service
@ -71,7 +85,7 @@ def init():
disable=disable) disable=disable)
if is_enabled(): if is_enabled():
add_shortcut() add_shortcut()
menu.promote_item('deluge:index') app.set_enabled(True)
def setup(helper, old_version=None): def setup(helper, old_version=None):
@ -86,8 +100,7 @@ def setup(helper, old_version=None):
disable=disable) disable=disable)
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut) helper.call('post', add_shortcut)
menu = main_menu.get('apps') helper.call('post', app.enable)
helper.call('post', menu.promote_item, 'deluge:index')
def add_shortcut(): def add_shortcut():
@ -105,16 +118,14 @@ def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('deluge', ['enable']) actions.superuser_run('deluge', ['enable'])
add_shortcut() add_shortcut()
menu = main_menu.get('apps') app.enable()
menu.promote_item('deluge:index')
def disable(): def disable():
"""Disable the module.""" """Disable the module."""
actions.superuser_run('deluge', ['disable']) actions.superuser_run('deluge', ['disable'])
frontpage.remove_shortcut('deluge') frontpage.remove_shortcut('deluge')
menu = main_menu.get('apps') app.disable()
menu.demote_item('deluge:index')
def diagnose(): def diagnose():

View File

@ -21,7 +21,8 @@ FreedomBox app for system diagnostics.
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth import action_utils 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 from .manifest import backup
@ -39,11 +40,25 @@ description = [
manual_page = 'Diagnostics' 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(): def init():
"""Initialize the module""" """Initialize the module"""
menu = main_menu.get('system') global app
menu.add_urlname(name, 'fa-heartbeat', 'diagnostics:index') app = DiagnosticsApp()
app.set_enabled(True)
def diagnose(): def diagnose():

View File

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

View File

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

View File

@ -23,9 +23,10 @@ import logging
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _ 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 import service as service_module
from plinth.menu import main_menu
from plinth.modules import config from plinth.modules import config
from plinth.signals import (domainname_change, post_hostname_change, from plinth.signals import (domainname_change, post_hostname_change,
pre_hostname_change) pre_hostname_change)
@ -72,11 +73,25 @@ port_forwarding_info = [
logger = logging.getLogger(__name__) 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(): def init():
"""Initialize the ejabberd module""" """Initialize the ejabberd module"""
menu = main_menu.get('apps') global app
menu.add_urlname(name, 'ejabberd', 'ejabberd:index', short_description) app = EjabberdApp()
global service global service
setup_helper = globals()['setup_helper'] setup_helper = globals()['setup_helper']
@ -88,7 +103,7 @@ def init():
enable=enable, disable=disable) enable=enable, disable=disable)
if is_enabled(): if is_enabled():
add_shortcut() add_shortcut()
menu.promote_item('ejabberd:index') app.set_enabled(True)
pre_hostname_change.connect(on_pre_hostname_change) pre_hostname_change.connect(on_pre_hostname_change)
post_hostname_change.connect(on_post_hostname_change) post_hostname_change.connect(on_post_hostname_change)
@ -113,8 +128,7 @@ def setup(helper, old_version=None):
enable=enable, disable=disable) enable=enable, disable=disable)
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut) helper.call('post', add_shortcut)
menu = main_menu.get('apps') helper.call('post', app.enable)
helper.call('post', menu.promote_item, 'ejabberd:index')
def add_shortcut(): def add_shortcut():
@ -133,16 +147,14 @@ def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('ejabberd', ['enable']) actions.superuser_run('ejabberd', ['enable'])
add_shortcut() add_shortcut()
menu = main_menu.get('apps') app.enable()
menu.promote_item('ejabberd:index')
def disable(): def disable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('ejabberd', ['disable']) actions.superuser_run('ejabberd', ['disable'])
frontpage.remove_shortcut('ejabberd') frontpage.remove_shortcut('ejabberd')
menu = main_menu.get('apps') app.disable()
menu.demote_item('ejabberd:index')
def on_pre_hostname_change(sender, old_hostname, new_hostname, **kwargs): 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 _ from django.utils.translation import ugettext_lazy as _
import plinth.service as service_module import plinth.service as service_module
from plinth import actions, cfg from plinth import actions
from plinth.menu import main_menu from plinth import app as app_module
from plinth import cfg, menu
from plinth.signals import service_enabled from plinth.signals import service_enabled
from plinth.utils import Version, format_lazy from plinth.utils import Version, format_lazy
@ -52,11 +53,25 @@ LOGGER = logging.getLogger(__name__)
_port_details = {} _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(): def init():
"""Initailze firewall module""" """Initailze firewall module"""
menu = main_menu.get('system') global app
menu.add_urlname(name, 'fa-shield', 'firewall:index') app = FirewallApp()
app.set_enabled(True)
service_enabled.connect(on_service_enabled) 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 as _
from django.utils.translation import ugettext_lazy from django.utils.translation import ugettext_lazy
from plinth import __version__, actions, cfg from plinth import __version__, actions
from plinth.menu import main_menu 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(): def init():
"""Initialize the Help module""" """Initialize the Help module"""
menu = main_menu.add_urlname( global app
ugettext_lazy('Documentation'), 'fa-book', 'help:index') app = HelpApp()
menu.add_urlname( app.set_enabled(True)
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)
def index(request): def index(request):

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -22,9 +22,10 @@ import augeas
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _ 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 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.utils import format_lazy
from .manifest import backup, clients from .manifest import backup, clients
@ -73,11 +74,25 @@ reserved_usernames = ['Debian-minetest']
CONFIG_FILE = '/etc/minetest/minetest.conf' CONFIG_FILE = '/etc/minetest/minetest.conf'
AUG_PATH = '/files' + CONFIG_FILE + '/.anon' 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(): def init():
"""Initialize the module.""" """Initialize the module."""
menu = main_menu.get('apps') global app
menu.add_urlname(name, 'minetest', 'minetest:index', short_description) app = MinetestApp()
global service global service
setup_helper = globals()['setup_helper'] setup_helper = globals()['setup_helper']
@ -87,7 +102,7 @@ def init():
], is_external=True, enable=enable, disable=disable) ], is_external=True, enable=enable, disable=disable)
if service.is_enabled(): if service.is_enabled():
add_shortcut() add_shortcut()
menu.promote_item('minetest:index') app.set_enabled(True)
def setup(helper, old_version=None): def setup(helper, old_version=None):
@ -100,8 +115,7 @@ def setup(helper, old_version=None):
], is_external=True, enable=enable, disable=disable) ], is_external=True, enable=enable, disable=disable)
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut) helper.call('post', add_shortcut)
menu = main_menu.get('apps') helper.call('post', app.enable)
helper.call('post', menu.promote_item, 'minetest:index')
def add_shortcut(): def add_shortcut():
@ -115,16 +129,14 @@ def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('service', ['enable', managed_services[0]]) actions.superuser_run('service', ['enable', managed_services[0]])
add_shortcut() add_shortcut()
menu = main_menu.get('apps') app.enable()
menu.promote_item('minetest:index')
def disable(): def disable():
"""Disable the module.""" """Disable the module."""
actions.superuser_run('service', ['disable', managed_services[0]]) actions.superuser_run('service', ['disable', managed_services[0]])
frontpage.remove_shortcut('minetest') frontpage.remove_shortcut('minetest')
menu = main_menu.get('apps') app.disable()
menu.demote_item('minetest:index')
def diagnose(): def diagnose():

View File

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

View File

@ -20,7 +20,8 @@ FreedomBox app for monkeysphere.
from django.utils.translation import ugettext_lazy as _ 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 from .manifest import backup
@ -53,12 +54,26 @@ manual_page = "Monkeysphere"
reserved_usernames = ['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(): def init():
"""Initialize the monkeysphere module.""" """Initialize the monkeysphere module."""
menu = main_menu.get('system') global app
menu.add_urlname( app = MonkeysphereApp()
_('Monkeysphere'), 'fa-certificate', 'monkeysphere:index') app.set_enabled(True)
def setup(helper, old_version=None): 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.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _ 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 service as service_module
from plinth import action_utils, actions, frontpage
from plinth.menu import main_menu
from plinth.views import ServiceView from plinth.views import ServiceView
from .manifest import backup, clients from .manifest import backup, clients
@ -59,11 +60,24 @@ port_forwarding_info = [
('UDP', 64738), ('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(): def init():
"""Intialize the Mumble module.""" """Intialize the Mumble module."""
menu = main_menu.get('apps') global app
menu.add_urlname(name, 'mumble', 'mumble:index', short_description) app = MumbleApp()
global service global service
setup_helper = globals()['setup_helper'] setup_helper = globals()['setup_helper']
@ -74,7 +88,7 @@ def init():
if service.is_enabled(): if service.is_enabled():
add_shortcut() add_shortcut()
menu.promote_item('mumble:index') app.set_enabled(True)
class MumbleServiceView(ServiceView): class MumbleServiceView(ServiceView):
@ -96,8 +110,7 @@ def setup(helper, old_version=None):
], is_external=True, enable=enable, disable=disable) ], is_external=True, enable=enable, disable=disable)
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut) helper.call('post', add_shortcut)
menu = main_menu.get('apps') helper.call('post', app.enable)
helper.call('post', menu.promote_item, 'mumble:index')
def add_shortcut(): def add_shortcut():
@ -111,16 +124,14 @@ def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('service', ['enable', managed_services[0]]) actions.superuser_run('service', ['enable', managed_services[0]])
add_shortcut() add_shortcut()
menu = main_menu.get('apps') app.enable()
menu.promote_item('mumble:index')
def disable(): def disable():
"""Disable the module.""" """Disable the module."""
actions.superuser_run('service', ['disable', managed_services[0]]) actions.superuser_run('service', ['disable', managed_services[0]])
frontpage.remove_shortcut('mumble') frontpage.remove_shortcut('mumble')
menu = main_menu.get('apps') app.disable()
menu.demote_item('mumble:index')
def diagnose(): def diagnose():

View File

@ -22,8 +22,8 @@ import logging
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth import cfg from plinth import app as app_module
from plinth.menu import main_menu from plinth import cfg, menu
from plinth.signals import domain_added, domain_removed from plinth.signals import domain_added, domain_removed
from plinth.utils import format_lazy from plinth.utils import format_lazy
@ -57,11 +57,25 @@ description = [
'connections through the given name.'), box_name=(cfg.box_name)) '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(): def init():
"""Initialize the names module.""" """Initialize the names module."""
menu = main_menu.get('system') global app
menu.add_urlname(name, 'fa-tags', 'names:index') app = NamesApp()
app.set_enabled(True)
domain_added.connect(on_domain_added) domain_added.connect(on_domain_added)
domain_removed.connect(on_domain_removed) 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 django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions, network from plinth import action_utils, actions
from plinth.menu import main_menu from plinth import app as app_module
from plinth import menu, network
version = 1 version = 1
@ -38,17 +39,32 @@ logger = Logger(__name__)
manual_page = 'Networks' 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(): def init():
"""Initialize the Networks module.""" """Initialize the Networks module."""
menu = main_menu.get('system') global app
menu.add_urlname(name, 'fa-signal', 'networks:index') app = NetworksApp()
app.set_enabled(True)
def setup(helper, old_version=None): def setup(helper, old_version=None):
"""Install and configure the module.""" """Install and configure the module."""
helper.install(managed_packages) helper.install(managed_packages)
actions.superuser_run('networks') actions.superuser_run('networks')
helper.call('post', app.enable)
def diagnose(): def diagnose():

View File

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

View File

@ -20,8 +20,8 @@ FreedomBox app to configure PageKite.
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth import cfg from plinth import app as app_module
from plinth.menu import main_menu from plinth import cfg, menu
from plinth.utils import format_lazy from plinth.utils import format_lazy
from . import utils from . import utils
@ -76,12 +76,30 @@ description = [
manual_page = 'PageKite' 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(): def init():
"""Intialize the PageKite module""" """Intialize the PageKite module"""
menu = main_menu.get('system') global app
menu.add_urlname(name, 'fa-flag', 'pagekite:index', app = PagekiteApp()
short_description)
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. # Register kite name with Name Services module.
utils.update_names_module(initial_registration=True) utils.update_names_module(initial_registration=True)
@ -90,3 +108,4 @@ def init():
def setup(helper, old_version=None): def setup(helper, old_version=None):
"""Install and configure the module.""" """Install and configure the module."""
helper.install(managed_packages) 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.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _ 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 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.utils import format_lazy
from plinth.views import ServiceView from plinth.views import ServiceView
@ -62,11 +63,25 @@ service = None
manual_page = 'Privoxy' 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(): def init():
"""Intialize the module.""" """Intialize the module."""
menu = main_menu.get('apps') global app
menu.add_urlname(name, 'privoxy', 'privoxy:index', short_description) app = PrivoxyApp()
global service global service
setup_helper = globals()['setup_helper'] setup_helper = globals()['setup_helper']
@ -77,7 +92,7 @@ def init():
if service.is_enabled(): if service.is_enabled():
add_shortcut() add_shortcut()
menu.promote_item('privoxy:index') app.set_enabled(True)
def setup(helper, old_version=None): def setup(helper, old_version=None):
@ -91,8 +106,7 @@ def setup(helper, old_version=None):
enable=enable, disable=disable) enable=enable, disable=disable)
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut) helper.call('post', add_shortcut)
menu = main_menu.get('apps') helper.call('post', app.enable)
helper.call('post', menu.promote_item, 'privoxy:index')
def add_shortcut(): def add_shortcut():
@ -106,16 +120,14 @@ def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('service', ['enable', managed_services[0]]) actions.superuser_run('service', ['enable', managed_services[0]])
add_shortcut() add_shortcut()
menu = main_menu.get('apps') app.enable()
menu.promote_item('privoxy:index')
def disable(): def disable():
"""Disable the module.""" """Disable the module."""
actions.superuser_run('service', ['disable', managed_services[0]]) actions.superuser_run('service', ['disable', managed_services[0]])
frontpage.remove_shortcut('privoxy') frontpage.remove_shortcut('privoxy')
menu = main_menu.get('apps') app.disable()
menu.demote_item('privoxy:index')
class PrivoxyServiceView(ServiceView): class PrivoxyServiceView(ServiceView):

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -20,9 +20,10 @@ FreedomBox app to configure Searx.
from django.utils.translation import ugettext_lazy as _ 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 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 plinth.modules.users import register_group
from .manifest import backup, clients from .manifest import backup, clients
@ -52,11 +53,24 @@ service = None
manual_page = 'Searx' 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(): def init():
"""Intialize the module.""" """Intialize the module."""
menu = main_menu.get('apps') global app
menu.add_urlname(name, 'searx', 'searx:index', short_description) app = SearxApp()
register_group(group) register_group(group)
global service global service
@ -69,7 +83,7 @@ def init():
if is_enabled(): if is_enabled():
add_shortcut() add_shortcut()
menu.promote_item('searx:index') app.set_enabled(True)
def setup(helper, old_version=None): 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, ], is_external=True, is_enabled=is_enabled, enable=enable,
disable=disable) disable=disable)
helper.call('post', add_shortcut) helper.call('post', add_shortcut)
menu = main_menu.get('apps') helper.call('post', app.enable)
helper.call('post', menu.promote_item, 'searx:index')
def add_shortcut(): def add_shortcut():
@ -113,16 +126,14 @@ def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('searx', ['enable']) actions.superuser_run('searx', ['enable'])
add_shortcut() add_shortcut()
menu = main_menu.get('apps') app.enable()
menu.promote_item('searx:index')
def disable(): def disable():
"""Disable the module.""" """Disable the module."""
actions.superuser_run('searx', ['disable']) actions.superuser_run('searx', ['disable'])
frontpage.remove_shortcut('searx') frontpage.remove_shortcut('searx')
menu = main_menu.get('apps') app.disable()
menu.demote_item('searx:index')
def diagnose(): def diagnose():

View File

@ -21,7 +21,8 @@ FreedomBox app for security configuration.
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth import actions 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 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' OLD_ACCESS_CONF_SNIPPET = '-:ALL EXCEPT root fbx (admin) (sudo):ALL'
ACCESS_CONF_SNIPPETS = [OLD_ACCESS_CONF_SNIPPET, ACCESS_CONF_SNIPPET] 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(): def init():
"""Initialize the module""" """Initialize the module"""
menu = main_menu.get('system') global app
menu.add_urlname(name, 'fa-lock', 'security:index') app = SecurityApp()
app.set_enabled(True)
def setup(helper, old_version=None): 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 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 service as service_module
from plinth import action_utils, actions, frontpage
from plinth.menu import main_menu
from .manifest import clients from .manifest import clients
@ -48,12 +49,25 @@ service = None
manual_page = 'Shaarli' 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(): def init():
"""Initialize the module.""" """Initialize the module."""
menu = main_menu.get('apps') global app
menu.add_urlname(name, 'fa-bookmark', 'shaarli:index', app = ShaarliApp()
short_description)
global service global service
setup_helper = globals()['setup_helper'] setup_helper = globals()['setup_helper']
@ -64,7 +78,7 @@ def init():
if is_enabled(): if is_enabled():
add_shortcut() add_shortcut()
menu.promote_item('shaarli:index') app.set_enabled(True)
def setup(helper, old_version=None): def setup(helper, old_version=None):
@ -77,8 +91,7 @@ def setup(helper, old_version=None):
is_enabled=is_enabled, enable=enable, disable=disable) is_enabled=is_enabled, enable=enable, disable=disable)
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut) helper.call('post', add_shortcut)
menu = main_menu.get('apps') helper.call('post', app.enable)
helper.call('post', menu.promote_item, 'shaarli:index')
def add_shortcut(): def add_shortcut():
@ -96,13 +109,11 @@ def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('shaarli', ['enable']) actions.superuser_run('shaarli', ['enable'])
add_shortcut() add_shortcut()
menu = main_menu.get('apps') app.enable()
menu.promote_item('shaarli:index')
def disable(): def disable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('shaarli', ['disable']) actions.superuser_run('shaarli', ['disable'])
frontpage.remove_shortcut('shaarli') frontpage.remove_shortcut('shaarli')
menu = main_menu.get('apps') app.disable()
menu.demote_item('shaarli:index')

View File

@ -21,9 +21,10 @@ FreedomBox app to configure Shadowsocks.
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _ 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 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.utils import format_lazy
from .manifest import backup from .manifest import backup
@ -56,12 +57,25 @@ description = [
manual_page = 'Shadowsocks' 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(): def init():
"""Intialize the module.""" """Intialize the module."""
menu = main_menu.get('apps') global app
menu.add_urlname(name, 'shadowsocks', 'shadowsocks:index', app = ShadowsocksApp()
short_description)
global service global service
setup_helper = globals()['setup_helper'] setup_helper = globals()['setup_helper']
@ -73,7 +87,7 @@ def init():
if service.is_enabled(): if service.is_enabled():
add_shortcut() add_shortcut()
menu.promote_item('shadowsocks:index') app.set_enabled(True)
def setup(helper, old_version=None): 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, ], is_external=False, is_enabled=is_enabled, is_running=is_running,
enable=enable, disable=disable) enable=enable, disable=disable)
menu = main_menu.get('apps') helper.call('post', app.enable)
helper.call('post', menu.promote_item, 'shadowsocks:index')
def add_shortcut(): def add_shortcut():
@ -113,16 +126,14 @@ def enable():
"""Enable service.""" """Enable service."""
actions.superuser_run('service', ['enable', managed_services[0]]) actions.superuser_run('service', ['enable', managed_services[0]])
add_shortcut() add_shortcut()
menu = main_menu.get('apps') app.enable()
menu.promote_item('shadowsocks:index')
def disable(): def disable():
"""Disable service.""" """Disable service."""
actions.superuser_run('service', ['disable', managed_services[0]]) actions.superuser_run('service', ['disable', managed_services[0]])
frontpage.remove_shortcut('shadowsocks') frontpage.remove_shortcut('shadowsocks')
menu = main_menu.get('apps') app.disable()
menu.demote_item('shadowsocks:index')
def diagnose(): def diagnose():

View File

@ -22,8 +22,9 @@ import json
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth import actions, cfg from plinth import actions
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 plinth.utils import format_lazy
from .manifest import backup from .manifest import backup
@ -35,16 +36,29 @@ name = _('Sharing')
description = [ description = [
format_lazy( format_lazy(
_('Sharing allows you to share files and folders on your {box_name} ' _('Sharing allows you to share files and folders on your {box_name} '
'over the web with chosen groups of users.'), box_name=_( 'over the web with chosen groups of users.'),
cfg.box_name)) 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(): def init():
"""Initialize the module.""" """Initialize the module."""
menu = main_menu.get('apps') global app
menu.add_urlname(name, 'sharing', 'sharing:index') app = SharingApp()
menu.promote_item('sharing:index') app.set_enabled(True)
def list_shares(): def list_shares():

View File

@ -24,7 +24,8 @@ import augeas
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth import actions 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 plinth.modules import storage
from .manifest import backup from .manifest import backup
@ -57,11 +58,29 @@ DEFAULT_FILE = '/etc/default/snapper'
fs_types_supported = ['btrfs'] 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(): def init():
"""Initialize the module.""" """Initialize the module."""
menu = main_menu.get('system') global app
menu.add_urlname(name, 'fa-film', 'snapshot:index') 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(): def is_supported():
@ -77,12 +96,13 @@ def setup(helper, old_version=None):
helper.call('post', actions.superuser_run, 'snapshot', helper.call('post', actions.superuser_run, 'snapshot',
['setup', '--old-version', ['setup', '--old-version',
str(old_version)]) str(old_version)])
helper.call('post', app.enable)
def load_augeas(): def load_augeas():
"""Initialize Augeas.""" """Initialize Augeas."""
aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD + aug = augeas.Augeas(
augeas.Augeas.NO_MODL_AUTOLOAD) flags=augeas.Augeas.NO_LOAD + augeas.Augeas.NO_MODL_AUTOLOAD)
# shell-script config file lens # shell-script config file lens
aug.set('/augeas/load/Shellvars/lens', 'Shellvars.lns') 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 django.utils.translation import ugettext_lazy as _
from plinth import actions 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 service as service_module
from plinth.menu import main_menu
from plinth.views import ServiceView from plinth.views import ServiceView
from .manifest import backup from .manifest import backup
@ -48,15 +49,29 @@ service = None
port_forwarding_info = [('TCP', 22)] 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(): def init():
"""Intialize the ssh module.""" """Intialize the ssh module."""
menu = main_menu.get('system') global app
menu.add_urlname(name, 'fa-terminal', 'ssh:index') app = SSHApp()
app.set_enabled(True)
global service global service
service = service_module.Service( service = service_module.Service(managed_services[0], name, ports=['ssh'],
managed_services[0], name, ports=['ssh'], is_external=True) is_external=True)
def setup(helper, old_version=None): def setup(helper, old_version=None):

View File

@ -23,11 +23,12 @@ import subprocess
import psutil import psutil
from django.utils.translation import ugettext_lazy as _ 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 service as service_module
from plinth import utils from plinth import utils
from plinth.errors import PlinthError from plinth.errors import PlinthError
from plinth.menu import main_menu
from plinth.utils import format_lazy, import_from_gi from plinth.utils import format_lazy, import_from_gi
version = 3 version = 3
@ -54,11 +55,25 @@ manual_page = 'Storage'
is_essential = True 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(): def init():
"""Intialize the module.""" """Intialize the module."""
menu = main_menu.get('system') global app
menu.add_urlname(name, 'fa-hdd-o', 'storage:index') app = StorageApp()
app.set_enabled(True)
def get_disks(): def get_disks():
@ -267,11 +282,13 @@ def is_enabled():
def enable(): def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('udiskie', ['enable']) actions.superuser_run('udiskie', ['enable'])
app.enable()
def disable(): def disable():
"""Disable the module.""" """Disable the module."""
actions.superuser_run('udiskie', ['disable']) actions.superuser_run('udiskie', ['disable'])
app.disable()
def setup(helper, old_version=None): 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 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 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.modules.users import register_group
from plinth.utils import format_lazy from plinth.utils import format_lazy
@ -64,11 +65,25 @@ service = None
manual_page = 'Syncthing' 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(): def init():
"""Intialize the module.""" """Intialize the module."""
menu = main_menu.get('apps') global app
menu.add_urlname(name, 'syncthing', 'syncthing:index', short_description) app = SyncthingApp()
register_group(group) register_group(group)
global service global service
@ -82,7 +97,7 @@ def init():
if is_enabled(): if is_enabled():
add_shortcut() add_shortcut()
menu.promote_item('syncthing:index') app.set_enabled(True)
def setup(helper, old_version=None): def setup(helper, old_version=None):
@ -98,8 +113,7 @@ def setup(helper, old_version=None):
is_running=is_running) is_running=is_running)
helper.call('post', service.notify_enabled, None, True) helper.call('post', service.notify_enabled, None, True)
helper.call('post', add_shortcut) helper.call('post', add_shortcut)
menu = main_menu.get('apps') helper.call('post', app.enable)
helper.call('post', menu.promote_item, 'syncthing:index')
def add_shortcut(): def add_shortcut():
@ -124,16 +138,14 @@ def enable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('syncthing', ['enable']) actions.superuser_run('syncthing', ['enable'])
add_shortcut() add_shortcut()
menu = main_menu.get('apps') app.enable()
menu.promote_item('syncthing:index')
def disable(): def disable():
"""Enable the module.""" """Enable the module."""
actions.superuser_run('syncthing', ['disable']) actions.superuser_run('syncthing', ['disable'])
frontpage.remove_shortcut('syncthing') frontpage.remove_shortcut('syncthing')
menu = main_menu.get('apps') app.disable()
menu.demote_item('syncthing:index')
def diagnose(): def diagnose():

View File

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

View File

@ -22,9 +22,10 @@ import json
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth import service as service_module
from plinth import action_utils, actions 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.modules.names import SERVICES
from plinth.signals import domain_added, domain_removed from plinth.signals import domain_added, domain_removed
@ -61,18 +62,31 @@ bridge_service = None
manual_page = 'Tor' 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(): def init():
"""Initialize the module.""" """Initialize the module."""
menu = main_menu.get('apps') global app
menu.add_urlname(name, 'tor', 'tor:index', short_description) app = TorApp()
setup_helper = globals()['setup_helper'] setup_helper = globals()['setup_helper']
needs_setup = setup_helper.get_state() == 'needs-setup' needs_setup = setup_helper.get_state() == 'needs-setup'
if not needs_setup: if not needs_setup:
if utils.is_enabled(): if utils.is_enabled():
menu.promote_item('tor:index') app.set_enabled(True)
global socks_service global socks_service
socks_service = service_module.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', bridge_service.notify_enabled, None, True)
helper.call('post', update_hidden_service_domain) helper.call('post', update_hidden_service_domain)
menu = main_menu.get('apps') helper.call('post', app.enable)
helper.call('post', menu.promote_item, 'tor:index')
def enable(): def enable():
@ -148,8 +161,7 @@ def enable():
elsewhere. elsewhere.
""" """
menu = main_menu.get('apps') app.enable()
menu.promote_item('tor:index')
def disable(): def disable():
@ -159,8 +171,7 @@ def disable():
elsewhere. elsewhere.
""" """
menu = main_menu.get('apps') app.disable()
menu.demote_item('tor:index')
def update_hidden_service_domain(status=None): def update_hidden_service_domain(status=None):

View File

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

View File

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

View File

@ -20,9 +20,10 @@ FreedomBox app for upgrades.
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth import service as service_module
from plinth import actions 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 from .manifest import backup
@ -42,11 +43,26 @@ service = None
manual_page = 'Upgrades' 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(): def init():
"""Initialize the module.""" """Initialize the module."""
menu = main_menu.get('system') global app
menu.add_urlname(name, 'fa-refresh', 'upgrades:index') app = UpgradesApp()
app.set_enabled(True)
global service global service
service = service_module.Service('auto-upgrades', name, is_external=False, service = service_module.Service('auto-upgrades', name, is_external=False,
is_enabled=is_enabled, enable=enable, is_enabled=is_enabled, enable=enable,

View File

@ -23,7 +23,8 @@ import subprocess
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from plinth import action_utils, actions 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 version = 2
@ -47,11 +48,25 @@ name = _('Users and Groups')
# All FreedomBox user groups # All FreedomBox user groups
groups = dict() 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(): def init():
"""Intialize the user module.""" """Intialize the user module."""
menu = main_menu.get('system') global app
menu.add_urlname(name, 'fa-users', 'users:index') app = UsersApp()
app.set_enabled(True)
def setup(helper, old_version=None): def setup(helper, old_version=None):

View File

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

View File

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

View File

@ -36,7 +36,7 @@
{% block content %} {% 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 %} {% for paragraph in setup_helper.module.description %}
<p>{{ paragraph|safe }}</p> <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 from unittest.mock import MagicMock, Mock
import pytest
from django.http import HttpRequest from django.http import HttpRequest
from plinth import cfg from plinth import cfg
from plinth import context_processors as cp 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(): def test_common():

View File

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

View File

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

View File

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