app: Add tags to menu and frontpage components

- Add typing information for init methods Info, Shortcut, and Menu to easily
  identify problems.

- Update docstrings for these components.

- Updates test cases to deal with tags instead of short description.

- Update custom shortcuts code to read tags and ignore short description.

- Update API to send tags instead of custom shortcuts.

- OpenVPN special treatment of info.description in Shortcut

Tests:

- All unit tests pass and type checking succeeds.

- All apps show icons with tags in apps and system section.

- In help section cards don't show tags.

- In front page, enabled apps show shortcuts with tags.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: Joseph Nuthalapati <njoseph@riseup.net>
This commit is contained in:
Sunil Mohan Adapa 2024-12-30 21:38:41 -08:00 committed by Joseph Nuthalapati
parent 196a2deb6f
commit 89bce7a344
No known key found for this signature in database
GPG Key ID: 5398F00A2FA43C35
75 changed files with 278 additions and 318 deletions

View File

@ -431,10 +431,14 @@ class LeaderComponent(Component):
class Info(FollowerComponent):
"""Component to capture basic information about an app."""
def __init__(self, app_id, version, is_essential=False, depends=None,
name=None, icon=None, icon_filename=None,
short_description=None, description=None, manual_page=None,
clients=None, donation_url=None, tags=None):
def __init__(self, app_id: str, version: int, is_essential: bool = False,
depends: list[str] | None = None, name: str | None = None,
icon: str | None = None, icon_filename: str | None = None,
description: list[str] | None = None,
manual_page: str | None = None,
clients: list[dict] | None = None,
donation_url: str | None = None,
tags: list[str] | None = None):
"""Store the basic properties of an app as a component.
Each app must contain at least one component of this type to provide
@ -480,12 +484,6 @@ class Info(FollowerComponent):
used in the primary app page and on the app listing page. Each app
typically has either an 'icon' or 'icon_filename' property set.
'short_description' is the user visible generic name of the app. For
example, for the 'Tor' app the short description is 'Anonymity
Network'. It is shown along with the name of the app in the list of
apps and when viewing the app's main page. It should be a lazily
translated Django string.
'description' is the user visible full description of the app. It is
shown along in the app page along with other app details. It should be
a list of lazily translated Django strings. Each string is rendered as
@ -516,7 +514,6 @@ class Info(FollowerComponent):
self.name = name
self.icon = icon
self.icon_filename = icon_filename
self.short_description = short_description
self.description = description
self.manual_page = manual_page
self.clients = clients

View File

@ -17,10 +17,14 @@ class Shortcut(app.FollowerComponent):
_all_shortcuts: ClassVar[dict[str, 'Shortcut']] = {}
def __init__(self, component_id, name, short_description=None, icon=None,
url=None, description=None, manual_page=None,
configure_url=None, clients=None, login_required=False,
allowed_groups=None):
def __init__(self, component_id: str, name: str | None,
icon: str | None = None, url: str | None = None,
description: list[str] | None = None,
manual_page: str | None = None,
configure_url: str | None = None,
clients: list[dict] | None = None,
tags: list[str] | None = None, login_required: bool = False,
allowed_groups: list[str] | None = None):
"""Initialize the frontpage shortcut component for an app.
When a user visits this web interface, they are first shown the
@ -34,8 +38,6 @@ class Shortcut(app.FollowerComponent):
'name' is the mandatory title for the shortcut.
'short_description' is an optional secondary title for the shortcut.
'icon' is used to find a suitable image to represent the shortcut.
'url' is link to which the user is redirected when the shortcut is
@ -60,6 +62,9 @@ class Shortcut(app.FollowerComponent):
service offered by the shortcut. This should be a valid client
information structure as validated by clients.py:validate().
'tags' is a list of tags that describe the app. Tags help users to find
similar apps or alternatives and discover use cases.
If 'login_required' is true, only logged-in users will be shown this
shortcut. Anonymous users visiting the frontpage won't be shown this
shortcut.
@ -77,13 +82,13 @@ class Shortcut(app.FollowerComponent):
url = '?selected={id}'.format(id=component_id)
self.name = name
self.short_description = short_description
self.url = url
self.icon = icon
self.description = description
self.manual_page = manual_page
self.configure_url = configure_url
self.clients = clients
self.tags = tags
self.login_required = login_required
self.allowed_groups = set(allowed_groups) if allowed_groups else None
@ -140,9 +145,10 @@ def add_custom_shortcuts():
shortcut_id = shortcut.get('id', shortcut['name'])
component_id = 'shortcut-custom-' + shortcut_id
tags = shortcut.get('tags', [])
component = Shortcut(component_id, shortcut['name'],
shortcut['short_description'],
icon=shortcut['icon_url'], url=web_app_url)
icon=shortcut['icon_url'], tags=tags,
url=web_app_url)
component.set_enabled(True)

View File

@ -13,15 +13,16 @@ class Menu(app.FollowerComponent):
_all_menus: ClassVar[set['Menu']] = set()
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, advanced=False):
def __init__(self, component_id: str, name: str | None = None,
icon: str | None = None, tags: list[str] | None = None,
url_name: str | None = None, url_args: list | None = None,
url_kwargs: dict | None = None,
parent_url_name: str | None = None, order: int = 50,
advanced: bool = False):
"""Initialize a new menu item with basic properties.
name is the label of the menu item.
short_description is an optional description shown on the menu item.
icon is the icon to be displayed for the menu item. Icon can be the
name of a glyphicon from the Fork Awesome font's icon set:
https://forkawesome.github.io/Fork-Awesome/icons/. In this case, the
@ -33,6 +34,9 @@ class Menu(app.FollowerComponent):
icons files plinth/modules/myapp/static/icons/myicon.svg and
plinth/modules/myapp/static/icons/myicon.png are used in the interface.
tags is a list of tags that describe the app. Tags help users to find
similar apps or alternatives and discover use cases.
url_name is the name of url location that will be activated when the
menu item is selected. This is not optional. url_args and url_kwargs
are sent to reverse() when resolving url from url_name.
@ -56,8 +60,8 @@ class Menu(app.FollowerComponent):
url = reverse_lazy(url_name, args=url_args, kwargs=url_kwargs)
self.name = name
self.short_description = short_description
self.icon = icon
self.tags = tags
self.url = url
self.order = order
self.advanced = advanced

View File

@ -48,10 +48,10 @@ def _get_shortcut_data(shortcut):
"""Return detailed information about a shortcut."""
shortcut_data = {
'name': shortcut.name,
'short_description': shortcut.short_description,
'description': shortcut.description,
'icon_url': _get_icon_url(shortcut.app_id, shortcut.icon),
'clients': copy.deepcopy(shortcut.clients),
'tags': copy.deepcopy(shortcut.tags),
}
# XXX: Fix the hardcoding
if shortcut.name.startswith('shortcut-ikiwiki-'):

View File

@ -50,7 +50,7 @@ class AvahiApp(app_module.App):
tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-avahi', info.name, None, info.icon,
menu_item = menu.Menu('menu-avahi', info.name, info.icon, info.tags,
'avahi:index',
parent_url_name='system:visibility', order=50)
self.add(menu_item)

View File

@ -47,7 +47,7 @@ class BackupsApp(app_module.App):
tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-backups', info.name, None, info.icon,
menu_item = menu.Menu('menu-backups', info.name, info.icon, info.tags,
'backups:index', parent_url_name='system:data',
order=20)
self.add(menu_item)

View File

@ -60,15 +60,14 @@ class BepastyApp(app_module.App):
clients=manifest.clients, tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-bepasty', info.name,
info.short_description, info.icon_filename,
'bepasty:index', parent_url_name='apps')
menu_item = menu.Menu('menu-bepasty', info.name, info.icon_filename,
info.tags, 'bepasty:index',
parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut('shortcut-bepasty', info.name,
info.short_description,
info.icon_filename, '/bepasty',
clients=manifest.clients)
clients=manifest.clients, tags=info.tags)
self.add(shortcut)
packages = Packages('packages-bepasty', ['bepasty'])

View File

@ -42,8 +42,8 @@ class BindApp(app_module.App):
tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-bind', info.name, info.short_description,
info.icon, 'bind:index',
menu_item = menu.Menu('menu-bind', info.name, info.icon, info.tags,
'bind:index',
parent_url_name='system:visibility', order=30)
self.add(menu_item)

View File

@ -59,15 +59,14 @@ class CalibreApp(app_module.App):
donation_url='https://calibre-ebook.com/donate')
self.add(info)
menu_item = menu.Menu('menu-calibre', info.name,
info.short_description, info.icon_filename,
'calibre:index', parent_url_name='apps')
menu_item = menu.Menu('menu-calibre', info.name, info.icon_filename,
info.tags, 'calibre:index',
parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut('shortcut-calibre', info.name,
short_description=info.short_description,
icon=info.icon_filename, url='/calibre',
clients=info.clients,
clients=info.clients, tags=info.tags,
login_required=True,
allowed_groups=list(groups))
self.add(shortcut)

View File

@ -56,18 +56,16 @@ class CockpitApp(app_module.App):
clients=manifest.clients, tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-cockpit', info.name,
info.short_description, info.icon,
menu_item = menu.Menu('menu-cockpit', info.name, info.icon, info.tags,
'cockpit:index',
parent_url_name='system:administration',
order=20)
self.add(menu_item)
shortcut = frontpage.Shortcut('shortcut-cockpit', info.name,
short_description=info.short_description,
icon=info.icon_filename,
url='/_cockpit/', clients=info.clients,
login_required=True,
tags=info.tags, login_required=True,
allowed_groups=['admin'])
self.add(shortcut)

View File

@ -42,9 +42,9 @@ class ConfigApp(app_module.App):
manual_page='Configure', tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-config', _('Configure'), None, info.icon,
'config:index', parent_url_name='system:system',
order=30)
menu_item = menu.Menu('menu-config', _('Configure'), info.icon,
info.tags, 'config:index',
parent_url_name='system:system', order=30)
self.add(menu_item)
packages = Packages('packages-config', ['zram-tools'])

View File

@ -54,8 +54,8 @@ class CoturnApp(app_module.App):
tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-coturn', info.name, info.short_description,
info.icon_filename, 'coturn:index',
menu_item = menu.Menu('menu-coturn', info.name, info.icon_filename,
info.tags, 'coturn:index',
parent_url_name='apps')
self.add(menu_item)

View File

@ -70,7 +70,7 @@ class DateTimeApp(app_module.App):
manual_page='DateTime', tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-datetime', info.name, None, info.icon,
menu_item = menu.Menu('menu-datetime', info.name, info.icon, info.tags,
'datetime:index',
parent_url_name='system:system', order=40)
self.add(menu_item)

View File

@ -64,15 +64,14 @@ class DelugeApp(app_module.App):
tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-deluge', info.name, info.short_description,
info.icon_filename, 'deluge:index',
menu_item = menu.Menu('menu-deluge', info.name, info.icon_filename,
info.tags, 'deluge:index',
parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut('shortcut-deluge', info.name,
short_description=info.short_description,
url='/deluge', icon=info.icon_filename,
clients=info.clients,
clients=info.clients, tags=info.tags,
login_required=True,
allowed_groups=list(groups))
self.add(shortcut)

View File

@ -55,8 +55,8 @@ class DiagnosticsApp(app_module.App):
manual_page='Diagnostics', tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-diagnostics', info.name, None, info.icon,
'diagnostics:index',
menu_item = menu.Menu('menu-diagnostics', info.name, info.icon,
info.tags, 'diagnostics:index',
parent_url_name='system:administration',
order=30)
self.add(menu_item)

View File

@ -63,8 +63,8 @@ class DynamicDNSApp(app_module.App):
manual_page='DynamicDNS', tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-dynamicdns', info.name, None, info.icon,
'dynamicdns:index',
menu_item = menu.Menu('menu-dynamicdns', info.name, info.icon,
info.tags, 'dynamicdns:index',
parent_url_name='system:visibility', order=20)
self.add(menu_item)

View File

@ -64,17 +64,16 @@ class EjabberdApp(app_module.App):
clients=manifest.clients, tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-ejabberd', info.name,
info.short_description, info.icon_filename,
'ejabberd:index', parent_url_name='apps')
menu_item = menu.Menu('menu-ejabberd', info.name, info.icon_filename,
info.tags, 'ejabberd:index',
parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-ejabberd', info.name,
short_description=info.short_description, icon=info.icon_filename,
'shortcut-ejabberd', info.name, icon=info.icon_filename,
description=info.description, manual_page=info.manual_page,
configure_url=reverse_lazy('ejabberd:index'), clients=info.clients,
login_required=True)
tags=info.tags, login_required=True)
self.add(shortcut)
packages = Packages('packages-ejabberd', ['ejabberd'])

View File

@ -5,6 +5,7 @@ import logging
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
from django.utils.translation import gettext_noop
import plinth.app
from plinth import cfg, frontpage, menu
@ -66,24 +67,23 @@ class EmailApp(plinth.app.App):
donation_url='https://rspamd.com/support.html')
self.add(info)
menu_item = menu.Menu('menu-email', info.name, info.short_description,
info.icon_filename, 'email:index',
parent_url_name='apps')
menu_item = menu.Menu('menu-email', info.name, info.icon_filename,
info.tags, 'email:index', parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-email', info.name,
short_description=info.short_description, icon=info.icon_filename,
'shortcut-email', info.name, icon=info.icon_filename,
description=info.description, manual_page=info.manual_page,
configure_url=reverse_lazy('email:index'), clients=info.clients,
login_required=True)
tags=info.tags, login_required=True)
self.add(shortcut)
shortcut = frontpage.Shortcut(
'shortcut-email-aliases', _('My Email Aliases'),
short_description=_('Manage Aliases for Mailbox'),
icon=info.icon_filename, url=reverse_lazy('email:aliases'),
login_required=True)
tags = [gettext_noop('More emails'), gettext_noop('Same mailbox')]
shortcut = frontpage.Shortcut('shortcut-email-aliases',
_('My Email Aliases'),
icon=info.icon_filename, tags=tags,
url=reverse_lazy('email:aliases'),
login_required=True)
self.add(shortcut)
# Other likely install conflicts have been discarded:

View File

@ -64,7 +64,7 @@ class FeatherWikiApp(app_module.App):
self.add(info)
menu_item = menu.Menu('menu-featherwiki', info.name,
info.short_description, info.icon_filename,
info.icon_filename, info.tags,
'featherwiki:index', parent_url_name='apps')
self.add(menu_item)
@ -72,11 +72,10 @@ class FeatherWikiApp(app_module.App):
# Expecting a large number of wiki files, so creating a shortcut for
# each file (like in ikiwiki's case) will crowd the front page.
shortcut = frontpage.Shortcut(
'shortcut-featherwiki', info.name,
short_description=info.short_description, icon=info.icon_filename,
'shortcut-featherwiki', info.name, icon=info.icon_filename,
description=info.description, manual_page=info.manual_page,
url='/featherwiki/', clients=info.clients, login_required=True,
allowed_groups=list(groups))
url='/featherwiki/', clients=info.clients, tags=info.tags,
login_required=True, allowed_groups=list(groups))
self.add(shortcut)
dropin_configs = DropinConfigs('dropin-configs-featherwiki', [

View File

@ -63,7 +63,7 @@ class FirewallApp(app_module.App):
manual_page='Firewall', tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-firewall', info.name, None, info.icon,
menu_item = menu.Menu('menu-firewall', info.name, info.icon, info.tags,
'firewall:index',
parent_url_name='system:security', order=30)
self.add(menu_item)

View File

@ -50,15 +50,14 @@ class GitwebApp(app_module.App):
clients=manifest.clients, tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-gitweb', info.name, info.short_description,
info.icon_filename, 'gitweb:index',
menu_item = menu.Menu('menu-gitweb', info.name, info.icon_filename,
info.tags, 'gitweb:index',
parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut('shortcut-gitweb', info.name,
short_description=info.short_description,
icon=info.icon_filename, url='/gitweb/',
clients=info.clients,
clients=info.clients, tags=info.tags,
login_required=True,
allowed_groups=list(groups))
self.add(shortcut)

View File

@ -30,27 +30,27 @@ class HelpApp(app_module.App):
is_essential=True)
self.add(info)
menu_item = menu.Menu('menu-help', _('Help'), None, 'fa-book',
menu_item = menu.Menu('menu-help', _('Help'), 'fa-book', None,
'help:index', parent_url_name='index')
self.add(menu_item)
menu_item = menu.Menu('menu-help-manual',
pgettext_lazy('User guide', 'Manual'), None,
'fa-info-circle', 'help:manual',
pgettext_lazy('User guide', 'Manual'),
'fa-info-circle', None, 'help:manual',
parent_url_name='help:index', order=10)
self.add(menu_item)
menu_item = menu.Menu('menu-help-support', _('Get Support'), None,
'fa-life-ring', 'help:support',
menu_item = menu.Menu('menu-help-support', _('Get Support'),
'fa-life-ring', None, 'help:support',
parent_url_name='help:index', order=20)
self.add(menu_item)
menu_item = menu.Menu('menu-help-feedback', _('Submit Feedback'), None,
'fa-comments', 'help:feedback',
menu_item = menu.Menu('menu-help-feedback', _('Submit Feedback'),
'fa-comments', None, 'help:feedback',
parent_url_name='help:index', order=25)
self.add(menu_item)
menu_item = menu.Menu('menu-help-contribute', _('Contribute'), None,
'fa-wrench', 'help:contribute',
menu_item = menu.Menu('menu-help-contribute', _('Contribute'),
'fa-wrench', None, 'help:contribute',
parent_url_name='help:index', order=30)
self.add(menu_item)
menu_item = menu.Menu('menu-help-about', _('About'), None, 'fa-star',
menu_item = menu.Menu('menu-help-about', _('About'), 'fa-star', None,
'help:about', parent_url_name='help:index',
order=100)
self.add(menu_item)

View File

@ -48,9 +48,9 @@ class IkiwikiApp(app_module.App):
donation_url='https://ikiwiki.info/tipjar/')
self.add(info)
menu_item = menu.Menu('menu-ikiwiki', info.name,
info.short_description, info.icon_filename,
'ikiwiki:index', parent_url_name='apps')
menu_item = menu.Menu('menu-ikiwiki', info.name, info.icon_filename,
info.tags, 'ikiwiki:index',
parent_url_name='apps')
self.add(menu_item)
packages = Packages('packages-ikiwiki', [

View File

@ -44,17 +44,16 @@ class InfinotedApp(app_module.App):
clients=manifest.clients, tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-infinoted', info.name,
info.short_description, info.icon_filename,
'infinoted:index', parent_url_name='apps')
menu_item = menu.Menu('menu-infinoted', info.name, info.icon_filename,
info.tags, 'infinoted:index',
parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-infinoted', info.name,
short_description=info.short_description, icon=info.icon_filename,
'shortcut-infinoted', info.name, icon=info.icon_filename,
description=info.description, manual_page=info.manual_page,
configure_url=reverse_lazy('infinoted:index'),
clients=info.clients, login_required=False)
clients=info.clients, tags=info.tags, login_required=False)
self.add(shortcut)
packages = Packages('packages-infinoted', ['infinoted'])

View File

@ -45,16 +45,14 @@ class JanusApp(app_module.App):
tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-janus', info.name, info.short_description,
info.icon_filename, 'janus:index',
parent_url_name='apps')
menu_item = menu.Menu('menu-janus', info.name, info.icon_filename,
info.tags, 'janus:index', parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut('shortcut-janus', info.name,
info.short_description,
info.icon_filename,
reverse_lazy('janus:room'),
clients=manifest.clients)
clients=manifest.clients, tags=info.tags)
self.add(shortcut)
packages = Packages('packages-janus', [

View File

@ -40,19 +40,17 @@ class JSXCApp(app_module.App):
clients=manifest.clients, tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-jsxc', info.name, info.short_description,
info.icon_filename, 'jsxc:index',
parent_url_name='apps')
menu_item = menu.Menu('menu-jsxc', info.name, info.icon_filename,
info.tags, 'jsxc:index', parent_url_name='apps')
self.add(menu_item)
enable_state = app_module.EnableState('enable-state-jsxc')
self.add(enable_state)
shortcut = frontpage.Shortcut('shortcut-jsxc', name=info.name,
short_description=info.short_description,
icon=info.icon_filename,
url=reverse_lazy('jsxc:jsxc'),
clients=info.clients)
clients=info.clients, tags=info.tags)
self.add(shortcut)
packages = Packages('packages-jsxc', ['libjs-jsxc'])

View File

@ -59,15 +59,13 @@ class KiwixApp(app_module.App):
donation_url='https://www.kiwix.org/en/support-us/')
self.add(info)
menu_item = menu.Menu('menu-kiwix', info.name, info.short_description,
info.icon_filename, 'kiwix:index',
parent_url_name='apps')
menu_item = menu.Menu('menu-kiwix', info.name, info.icon_filename,
info.tags, 'kiwix:index', parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut('shortcut-kiwix', info.name,
short_description=info.short_description,
icon=info.icon_filename, url='/kiwix',
clients=info.clients,
clients=info.clients, tags=info.tags,
login_required=False,
allowed_groups=list(groups))
self.add(shortcut)

View File

@ -63,9 +63,8 @@ class LetsEncryptApp(app_module.App):
donation_url='https://letsencrypt.org/donate/')
self.add(info)
menu_item = menu.Menu('menu-letsencrypt', info.name,
info.short_description, info.icon,
'letsencrypt:index',
menu_item = menu.Menu('menu-letsencrypt', info.name, info.icon,
info.tags, 'letsencrypt:index',
parent_url_name='system:security', order=20)
self.add(menu_item)

View File

@ -59,16 +59,15 @@ class MatrixSynapseApp(app_module.App):
self.add(info)
menu_item = menu.Menu('menu-matrixsynapse', info.name,
info.short_description, 'matrixsynapse',
info.icon_filename, info.tags,
'matrixsynapse:index', parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-matrixsynapse', info.name,
short_description=info.short_description, icon=info.icon_filename,
'shortcut-matrixsynapse', info.name, icon=info.icon_filename,
description=info.description, manual_page=info.manual_page,
configure_url=reverse_lazy('matrixsynapse:index'),
clients=info.clients, login_required=True)
clients=info.clients, tags=info.tags, login_required=True)
self.add(shortcut)
# Include python3-psycopg2 to prevent accidental uninstall

View File

@ -54,15 +54,15 @@ class MediaWikiApp(app_module.App):
clients=manifest.clients, tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-mediawiki', info.name,
info.short_description, info.icon_filename,
'mediawiki:index', parent_url_name='apps')
menu_item = menu.Menu('menu-mediawiki', info.name, info.icon_filename,
info.tags, 'mediawiki:index',
parent_url_name='apps')
self.add(menu_item)
shortcut = Shortcut('shortcut-mediawiki', info.name,
short_description=info.short_description,
icon=info.icon_filename, url='/mediawiki',
clients=info.clients, login_required=True)
clients=info.clients, tags=info.tags,
login_required=True)
self.add(shortcut)
packages = Packages('packages-mediawiki',

View File

@ -60,17 +60,16 @@ class MinetestApp(app_module.App):
donation_url='https://www.minetest.net/get-involved/#donate')
self.add(info)
menu_item = menu.Menu('menu-minetest', info.name,
info.short_description, info.icon_filename,
'minetest:index', parent_url_name='apps')
menu_item = menu.Menu('menu-minetest', info.name, info.icon_filename,
info.tags, 'minetest:index',
parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-minetest', info.name,
short_description=info.short_description, icon=info.icon_filename,
'shortcut-minetest', info.name, icon=info.icon_filename,
description=info.description, manual_page=info.manual_page,
configure_url=reverse_lazy('minetest:index'), clients=info.clients,
login_required=False)
tags=info.tags, login_required=False)
self.add(shortcut)
packages = Packages('packages-minetest', ['minetest-server'] + _mods)

View File

@ -48,21 +48,17 @@ class MiniDLNAApp(app_module.App):
clients=manifest.clients, tags=manifest.tags)
self.add(info)
menu_item = menu.Menu(
'menu-minidlna',
name=info.name,
short_description=info.short_description,
url_name='minidlna:index',
parent_url_name='apps',
icon=info.icon_filename,
)
menu_item = menu.Menu('menu-minidlna', name=info.name,
icon=info.icon_filename, tags=info.tags,
url_name='minidlna:index',
parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-minidlna', info.name,
short_description=info.short_description,
description=info.description, icon=info.icon_filename,
configure_url=reverse_lazy('minidlna:index'), login_required=True)
'shortcut-minidlna', info.name, description=info.description,
icon=info.icon_filename,
configure_url=reverse_lazy('minidlna:index'), tags=info.tags,
login_required=True)
self.add(shortcut)
packages = Packages('packages-minidlna', ['minidlna'])

View File

@ -47,15 +47,14 @@ class MinifluxApp(app_module.App):
donation_url='https://miniflux.app/#donations')
self.add(info)
menu_item = menu.Menu('menu-miniflux', info.name,
info.short_description, info.icon_filename,
'miniflux:index', parent_url_name='apps')
menu_item = menu.Menu('menu-miniflux', info.name, info.icon_filename,
info.tags, 'miniflux:index',
parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut('shortcut-miniflux', info.name,
info.short_description,
info.icon_filename, url='/miniflux',
clients=manifest.clients,
clients=manifest.clients, tags=info.tags,
login_required=True)
self.add(shortcut)

View File

@ -50,15 +50,16 @@ class MumbleApp(app_module.App):
donation_url='https://wiki.mumble.info/wiki/Donate')
self.add(info)
menu_item = menu.Menu('menu-mumble', info.name, info.short_description,
'mumble', 'mumble:index', parent_url_name='apps')
menu_item = menu.Menu('menu-mumble', info.name, info.icon_filename,
info.tags, 'mumble:index',
parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-mumble', info.name,
short_description=info.short_description, icon=info.icon_filename,
'shortcut-mumble', info.name, icon=info.icon_filename,
description=info.description, manual_page=info.manual_page,
configure_url=reverse_lazy('mumble:index'), clients=info.clients)
configure_url=reverse_lazy('mumble:index'), clients=info.clients,
tags=info.tags)
self.add(shortcut)
packages = Packages('packages-mumble', ['mumble-server'])

View File

@ -56,7 +56,7 @@ class NamesApp(app_module.App):
manual_page='NameServices', tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-names', info.name, None, info.icon,
menu_item = menu.Menu('menu-names', info.name, info.icon, info.tags,
'names:index',
parent_url_name='system:visibility', order=10)
self.add(menu_item)

View File

@ -44,7 +44,7 @@ class NetworksApp(app_module.App):
manual_page='Networks', tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-networks', info.name, None, info.icon,
menu_item = menu.Menu('menu-networks', info.name, info.icon, info.tags,
'networks:index',
parent_url_name='system:system', order=20)
self.add(menu_item)

View File

@ -68,16 +68,15 @@ class NextcloudApp(app_module.App):
clients=manifest.clients, tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-nextcloud', info.name,
info.short_description, info.icon_filename,
'nextcloud:index', parent_url_name='apps')
menu_item = menu.Menu('menu-nextcloud', info.name, info.icon_filename,
info.tags, 'nextcloud:index',
parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut('shortcut-nextcloud', info.name,
short_description=info.short_description,
icon=info.icon_filename,
url='/nextcloud/', clients=info.clients,
login_required=True)
tags=info.tags, login_required=True)
self.add(shortcut)
packages = Packages('packages-nextcloud', [

View File

@ -48,9 +48,9 @@ class OpenVPNApp(app_module.App):
clients=manifest.clients, tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-openvpn', info.name,
info.short_description, info.icon_filename,
'openvpn:index', parent_url_name='apps')
menu_item = menu.Menu('menu-openvpn', info.name, info.icon_filename,
info.tags, 'openvpn:index',
parent_url_name='apps')
self.add(menu_item)
download_profile = \
@ -58,12 +58,11 @@ class OpenVPNApp(app_module.App):
'Download Profile</a>'),
link=reverse_lazy('openvpn:profile'))
shortcut = frontpage.Shortcut(
'shortcut-openvpn', info.name,
short_description=info.short_description, icon=info.icon_filename,
description=info.description + [download_profile],
'shortcut-openvpn', info.name, icon=info.icon_filename,
description=(info.description or []) + [download_profile],
manual_page=info.manual_page,
configure_url=reverse_lazy('openvpn:index'), login_required=True,
allowed_groups=['vpn'])
configure_url=reverse_lazy('openvpn:index'), tags=info.tags,
login_required=True, allowed_groups=['vpn'])
self.add(shortcut)
packages = Packages('packages-openvpn',

View File

@ -61,8 +61,7 @@ class PagekiteApp(app_module.App):
donation_url='https://pagekite.net/support/faq/#donate')
self.add(info)
menu_item = menu.Menu('menu-pagekite', info.name,
info.short_description, info.icon,
menu_item = menu.Menu('menu-pagekite', info.name, info.icon, info.tags,
'pagekite:index',
parent_url_name='system:visibility', order=40)
self.add(menu_item)

View File

@ -43,9 +43,8 @@ class PerformanceApp(app_module.App):
clients=manifest.clients, tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-performance', info.name,
info.short_description, info.icon,
'performance:index',
menu_item = menu.Menu('menu-performance', info.name, info.icon,
info.tags, 'performance:index',
parent_url_name='system:administration',
order=40)
self.add(menu_item)

View File

@ -33,7 +33,7 @@ class PowerApp(app_module.App):
manual_page='Power', tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-power', info.name, None, info.icon,
menu_item = menu.Menu('menu-power', info.name, info.icon, info.tags,
'power:index',
parent_url_name='system:administration',
order=50)

View File

@ -34,8 +34,7 @@ class PrivacyApp(app_module.App):
manual_page=None, tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-privacy', info.name,
info.short_description, info.icon,
menu_item = menu.Menu('menu-privacy', info.name, info.icon, info.tags,
'privacy:index', parent_url_name='system:data',
order=10)
self.add(menu_item)

View File

@ -57,16 +57,16 @@ class PrivoxyApp(app_module.App):
tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-privoxy', info.name,
info.short_description, info.icon_filename,
'privoxy:index', parent_url_name='apps')
menu_item = menu.Menu('menu-privoxy', info.name, info.icon_filename,
info.tags, 'privoxy:index',
parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-privoxy', info.name,
short_description=info.short_description, icon=info.icon_filename,
'shortcut-privoxy', info.name, icon=info.icon_filename,
description=info.description, manual_page=info.manual_page,
configure_url=reverse_lazy('privoxy:index'), login_required=True)
configure_url=reverse_lazy('privoxy:index'), tags=info.tags,
login_required=True)
self.add(shortcut)
packages = Packages('packages-privoxy', ['privoxy'])

View File

@ -53,17 +53,16 @@ class QuasselApp(app_module.App):
clients=manifest.clients, tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-quassel', info.name,
info.short_description, info.icon_filename,
'quassel:index', parent_url_name='apps')
menu_item = menu.Menu('menu-quassel', info.name, info.icon_filename,
info.tags, 'quassel:index',
parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-quassel', info.name,
short_description=info.short_description, icon=info.icon_filename,
'shortcut-quassel', info.name, icon=info.icon_filename,
description=info.description, manual_page=info.manual_page,
configure_url=reverse_lazy('quassel:index'), clients=info.clients,
login_required=True)
tags=info.tags, login_required=True)
self.add(shortcut)
packages = Packages('packages-quassel', ['quassel-core'])

View File

@ -56,16 +56,15 @@ class RadicaleApp(app_module.App):
clients=manifest.clients, tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-radicale', info.name,
info.short_description, info.icon_filename,
'radicale:index', parent_url_name='apps')
menu_item = menu.Menu('menu-radicale', info.name, info.icon_filename,
info.tags, 'radicale:index',
parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut('shortcut-radicale', info.name,
short_description=info.short_description,
icon=info.icon_filename,
url='/radicale/', clients=info.clients,
login_required=True)
tags=info.tags, login_required=True)
self.add(shortcut)
packages = Packages('packages-radicale', ['radicale'])

View File

@ -53,16 +53,15 @@ class RoundcubeApp(app_module.App):
clients=manifest.clients, tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-roundcube', info.name,
info.short_description, info.icon_filename,
'roundcube:index', parent_url_name='apps')
menu_item = menu.Menu('menu-roundcube', info.name, info.icon_filename,
info.tags, 'roundcube:index',
parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut('shortcut-roundcube', info.name,
short_description=info.short_description,
icon=info.icon_filename,
url='/roundcube/', clients=info.clients,
login_required=True)
tags=info.tags, login_required=True)
self.add(shortcut)
packages = Packages(

View File

@ -54,15 +54,15 @@ class RSSBridgeApp(app_module.App):
clients=manifest.clients, tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-rssbridge', info.name,
info.short_description, info.icon_filename,
'rssbridge:index', parent_url_name='apps')
menu_item = menu.Menu('menu-rssbridge', info.name, info.icon_filename,
info.tags, 'rssbridge:index',
parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut('shortcut-rssbridge', name=info.name,
short_description=info.short_description,
icon=info.icon_filename,
url='/rss-bridge/', login_required=True,
url='/rss-bridge/', tags=info.tags,
login_required=True,
allowed_groups=list(groups))
self.add(shortcut)

View File

@ -57,17 +57,15 @@ class SambaApp(app_module.App):
donation_url='https://www.samba.org/samba/donations.html')
self.add(info)
menu_item = menu.Menu('menu-samba', info.name, info.short_description,
info.icon_filename, 'samba:index',
parent_url_name='apps')
menu_item = menu.Menu('menu-samba', info.name, info.icon_filename,
info.tags, 'samba:index', parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-samba', info.name,
short_description=info.short_description, icon=info.icon_filename,
'shortcut-samba', info.name, icon=info.icon_filename,
description=info.description, manual_page=info.manual_page,
configure_url=reverse_lazy('samba:index'), clients=info.clients,
login_required=True, allowed_groups=list(groups))
tags=info.tags, login_required=True, allowed_groups=list(groups))
self.add(shortcut)
packages = Packages('packages-samba', ['samba', 'acl'])

View File

@ -44,15 +44,13 @@ class SearxApp(app_module.App):
donation_url='https://searx.me/static/donate.html')
self.add(info)
menu_item = menu.Menu('menu-searx', info.name, info.short_description,
info.icon_filename, 'searx:index',
parent_url_name='apps')
menu_item = menu.Menu('menu-searx', info.name, info.icon_filename,
info.tags, 'searx:index', parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-searx', info.name,
short_description=info.short_description, icon=info.icon_filename,
url='/searx/', clients=info.clients,
'shortcut-searx', info.name, icon=info.icon_filename,
url='/searx/', clients=info.clients, tags=info.tags,
login_required=(not is_public_access_enabled()),
allowed_groups=list(groups))
self.add(shortcut)

View File

@ -37,7 +37,7 @@ class SecurityApp(app_module.App):
tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-security', info.name, None, info.icon,
menu_item = menu.Menu('menu-security', info.name, info.icon, info.tags,
'security:index',
parent_url_name='system:security', order=10)
self.add(menu_item)

View File

@ -38,15 +38,14 @@ class ShaarliApp(app_module.App):
clients=manifest.clients, tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-shaarli', info.name,
info.short_description, info.icon_filename,
'shaarli:index', parent_url_name='apps')
menu_item = menu.Menu('menu-shaarli', info.name, info.icon_filename,
info.tags, 'shaarli:index',
parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut('shortcut-shaarli', info.name,
short_description=info.short_description,
icon=info.icon_filename, url='/shaarli',
clients=info.clients,
clients=info.clients, tags=info.tags,
login_required=True)
self.add(shortcut)

View File

@ -54,15 +54,14 @@ class ShadowsocksApp(app_module.App):
self.add(info)
menu_item = menu.Menu('menu-shadowsocks', info.name,
info.short_description, info.icon_filename,
info.icon_filename, info.tags,
'shadowsocks:index', parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-shadowsocks', info.name,
short_description=info.short_description, icon=info.icon_filename,
'shortcut-shadowsocks', info.name, icon=info.icon_filename,
description=info.description, manual_page=info.manual_page,
configure_url=reverse_lazy('shadowsocks:index'),
configure_url=reverse_lazy('shadowsocks:index'), tags=info.tags,
login_required=True)
self.add(shortcut)

View File

@ -51,17 +51,16 @@ class ShadowsocksServerApp(app_module.App):
self.add(info)
menu_item = menu.Menu('menu-shadowsocks-server', info.name,
info.short_description, info.icon_filename,
info.icon_filename, info.tags,
'shadowsocksserver:index',
parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-shadowsocks-server', info.name,
short_description=info.short_description, icon=info.icon_filename,
'shortcut-shadowsocks-server', info.name, icon=info.icon_filename,
description=info.description, manual_page=info.manual_page,
configure_url=reverse_lazy('shadowsocksserver:index'),
login_required=True)
tags=info.tags, login_required=True)
self.add(shortcut)
packages = Packages('packages-shadowsocks-server',

View File

@ -36,8 +36,8 @@ class SharingApp(app_module.App):
tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-sharing', info.name, None,
info.icon_filename, 'sharing:index',
menu_item = menu.Menu('menu-sharing', info.name, info.icon_filename,
info.tags, 'sharing:index',
parent_url_name='apps')
self.add(menu_item)

View File

@ -52,7 +52,7 @@ class SnapshotApp(app_module.App):
manual_page='Snapshots', tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-snapshot', info.name, None, info.icon,
menu_item = menu.Menu('menu-snapshot', info.name, info.icon, info.tags,
'snapshot:index', parent_url_name='system:data',
order=40)
self.add(menu_item)

View File

@ -44,7 +44,7 @@ class SSHApp(app_module.App):
manual_page='SecureShell', tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-ssh', info.name, None, info.icon,
menu_item = menu.Menu('menu-ssh', info.name, info.icon, info.tags,
'ssh:index',
parent_url_name='system:administration',
order=10)

View File

@ -49,7 +49,7 @@ class StorageApp(app_module.App):
manual_page='Storage', tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-storage', info.name, None, info.icon,
menu_item = menu.Menu('menu-storage', info.name, info.icon, info.tags,
'storage:index', parent_url_name='system:data',
order=30)
self.add(menu_item)

View File

@ -64,16 +64,15 @@ class SyncthingApp(app_module.App):
tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-syncthing', info.name,
info.short_description, info.icon_filename,
'syncthing:index', parent_url_name='apps')
menu_item = menu.Menu('menu-syncthing', info.name, info.icon_filename,
info.tags, 'syncthing:index',
parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut('shortcut-syncthing', info.name,
short_description=info.short_description,
icon=info.icon_filename,
url='/syncthing/', clients=info.clients,
login_required=True,
tags=info.tags, login_required=True,
allowed_groups=list(self.groups))
self.add(shortcut)

View File

@ -68,20 +68,19 @@ class TiddlyWikiApp(app_module.App):
clients=manifest.clients, tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-tiddlywiki', info.name,
info.short_description, info.icon_filename,
'tiddlywiki:index', parent_url_name='apps')
menu_item = menu.Menu('menu-tiddlywiki', info.name, info.icon_filename,
info.tags, 'tiddlywiki:index',
parent_url_name='apps')
self.add(menu_item)
# The shortcut is a simple directory listing provided by Apache server.
# Expecting a large number of wiki files, so creating a shortcut for
# each file (like in ikiwiki's case) will crowd the front page.
shortcut = frontpage.Shortcut(
'shortcut-tiddlywiki', info.name,
short_description=info.short_description, icon=info.icon_filename,
'shortcut-tiddlywiki', info.name, icon=info.icon_filename,
description=info.description, manual_page=info.manual_page,
url='/tiddlywiki/', clients=info.clients, login_required=True,
allowed_groups=list(groups))
url='/tiddlywiki/', clients=info.clients, tags=info.tags,
login_required=True, allowed_groups=list(groups))
self.add(shortcut)
dropin_configs = DropinConfigs('dropin-configs-tiddlywiki', [

View File

@ -66,9 +66,8 @@ class TorApp(app_module.App):
donation_url='https://donate.torproject.org/')
self.add(info)
menu_item = menu.Menu('menu-tor', info.name, info.short_description,
info.icon_filename, 'tor:index',
parent_url_name='apps')
menu_item = menu.Menu('menu-tor', info.name, info.icon_filename,
info.tags, 'tor:index', parent_url_name='apps')
self.add(menu_item)
packages = Packages('packages-tor',

View File

@ -61,16 +61,16 @@ class TorProxyApp(app_module.App):
donation_url='https://donate.torproject.org/')
self.add(info)
menu_item = menu.Menu('menu-torproxy', info.name,
info.short_description, info.icon_filename,
'torproxy:index', parent_url_name='apps')
menu_item = menu.Menu('menu-torproxy', info.name, info.icon_filename,
info.tags, 'torproxy:index',
parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-torproxy', info.name,
short_description=info.short_description, icon=info.icon_filename,
'shortcut-torproxy', info.name, icon=info.icon_filename,
description=info.description, manual_page=info.manual_page,
configure_url=reverse_lazy('torproxy:index'), login_required=True)
configure_url=reverse_lazy('torproxy:index'), tags=info.tags,
login_required=True)
self.add(shortcut)
packages = Packages('packages-torproxy', [

View File

@ -79,15 +79,16 @@ class TransmissionApp(app_module.App):
self.add(info)
menu_item = menu.Menu('menu-transmission', info.name,
info.short_description, info.icon_filename,
info.icon_filename, info.tags,
'transmission:index', parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-transmission', info.name,
short_description=info.short_description, icon=info.icon_filename,
url='/transmission', clients=info.clients, login_required=True,
allowed_groups=list(groups))
shortcut = frontpage.Shortcut('shortcut-transmission', info.name,
icon=info.icon_filename,
url='/transmission',
clients=info.clients, tags=info.tags,
login_required=True,
allowed_groups=list(groups))
self.add(shortcut)
packages = Packages('packages-transmission', ['transmission-daemon'])

View File

@ -54,15 +54,13 @@ class TTRSSApp(app_module.App):
donation_url='https://www.patreon.com/cthulhoo')
self.add(info)
menu_item = menu.Menu('menu-ttrss', info.name, info.short_description,
info.icon_filename, 'ttrss:index',
parent_url_name='apps')
menu_item = menu.Menu('menu-ttrss', info.name, info.icon_filename,
info.tags, 'ttrss:index', parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut('shortcut-ttrss', info.name,
short_description=info.short_description,
icon=info.icon_filename, url='/tt-rss',
clients=info.clients,
clients=info.clients, tags=info.tags,
login_required=True,
allowed_groups=list(groups))
self.add(shortcut)

View File

@ -66,7 +66,7 @@ class UpgradesApp(app_module.App):
manual_page='Upgrades', tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-upgrades', info.name, None, info.icon,
menu_item = menu.Menu('menu-upgrades', info.name, info.icon, info.tags,
'upgrades:index',
parent_url_name='system:system', order=50)
self.add(menu_item)

View File

@ -61,7 +61,7 @@ class UsersApp(app_module.App):
manual_page='Users', tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-users', info.name, None, info.icon,
menu_item = menu.Menu('menu-users', info.name, info.icon, info.tags,
'users:index', parent_url_name='system:system',
order=10)
self.add(menu_item)

View File

@ -51,15 +51,15 @@ class WireguardApp(app_module.App):
donation_url='https://www.wireguard.com/donations/')
self.add(info)
menu_item = menu.Menu('menu-wireguard', info.name,
info.short_description, info.icon_filename,
'wireguard:index', parent_url_name='apps')
menu_item = menu.Menu('menu-wireguard', info.name, info.icon_filename,
info.tags, 'wireguard:index',
parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut(
'shortcut-wireguard', info.name,
short_description=info.short_description, icon=info.icon_filename,
description=info.description, manual_page=info.manual_page,
'shortcut-wireguard', info.name, icon=info.icon_filename,
tags=info.tags, description=info.description,
manual_page=info.manual_page,
configure_url=reverse_lazy('wireguard:index'), login_required=True,
clients=info.clients)
self.add(shortcut)

View File

@ -57,15 +57,15 @@ class WordPressApp(app_module.App):
donation_url='https://wordpressfoundation.org/donate/')
self.add(info)
menu_item = menu.Menu('menu-wordpress', info.name,
info.short_description, info.icon_filename,
'wordpress:index', parent_url_name='apps')
menu_item = menu.Menu('menu-wordpress', info.name, info.icon_filename,
info.tags, 'wordpress:index',
parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut('shortcut-wordpress', info.name,
short_description=info.short_description,
icon=info.icon_filename,
url='/wordpress/', clients=info.clients)
url='/wordpress/', clients=info.clients,
tags=info.tags)
self.add(shortcut)
# Add php to avoid wordpress package bringing in lib-apache2-mod-php.

View File

@ -58,15 +58,13 @@ class ZophApp(app_module.App):
clients=manifest.clients, tags=manifest.tags)
self.add(info)
menu_item = menu.Menu('menu-zoph', info.name, info.short_description,
info.icon_filename, 'zoph:index',
parent_url_name='apps')
menu_item = menu.Menu('menu-zoph', info.name, info.icon_filename,
info.tags, 'zoph:index', parent_url_name='apps')
self.add(menu_item)
shortcut = frontpage.Shortcut('shortcut-zoph', info.name,
short_description=info.short_description,
icon=info.icon_filename, url='/zoph/',
clients=info.clients,
clients=info.clients, tags=info.tags,
login_required=True)
self.add(shortcut)

View File

@ -1,6 +1,5 @@
{"shortcuts": [{
"name": "NextCloud",
"short_description": "File Hosting Service",
"description": [ "Nextcloud is a suite of client-server software for creating and using file hosting services." ],
"icon_url": "/plinth/custom/static/themes/default/icons/nextcloud.png",
"clients": [{

View File

@ -1,6 +1,5 @@
{"shortcuts": [{
"name": "NextCloud2",
"short_description": "File Hosting Service",
"description": [ "Nextcloud is a suite of client-server software for creating and using file hosting services." ],
"icon_url": "/plinth/custom/static/themes/default/icons/nextcloud.png",
"clients": [{
@ -9,5 +8,6 @@
"type": "web",
"url": "/nextcloud"
}]
}]
}],
"tags": ["Groupware", "File sync"]
}]}

View File

@ -1,6 +1,5 @@
{"shortcuts": [{
"name": "NextCloud",
"short_description": "File Hosting Service",
"description": [ "Nextcloud is a suite of client-server software for creating and using file hosting services." ],
"icon_url": "/plinth/custom/static/themes/default/icons/nextcloud.png",
"clients": [{
@ -9,5 +8,6 @@
"type": "web",
"url": "/nextcloud"
}]
}]
}],
"tags": ["Groupware", "File sync"]
}]}

View File

@ -438,10 +438,10 @@ def test_info_initialization_without_args():
assert info.name is None
assert info.icon is None
assert info.icon_filename is None
assert info.short_description is None
assert info.description is None
assert info.manual_page is None
assert info.clients is None
assert info.tags == []
def test_info_initialization_with_args():
@ -455,17 +455,17 @@ def test_info_initialization_with_args():
}]
info = Info('test-app', 3, is_essential=True, depends=['test-app-2'],
name='Test App', icon='fa-test', icon_filename='test-icon',
short_description='For Test', description='Test description',
manual_page='Test', clients=clients)
description='Test description', manual_page='Test',
clients=clients, tags=['tag1', 'tag2'])
assert info.is_essential
assert info.depends == ['test-app-2']
assert info.name == 'Test App'
assert info.icon == 'fa-test'
assert info.icon_filename == 'test-icon'
assert info.short_description == 'For Test'
assert info.description == 'Test description'
assert info.manual_page == 'Test'
assert info.clients == clients
assert info.tags == ['tag1', 'tag2']
def test_info_clients_validation():

View File

@ -26,13 +26,13 @@ def test_shortcut_init_with_arguments():
shortcut = Shortcut('test-component', 'test-name')
assert shortcut.component_id == 'test-component'
assert shortcut.name == 'test-name'
assert shortcut.short_description is None
assert shortcut.url == '?selected=test-component'
assert shortcut.icon is None
assert shortcut.description is None
assert shortcut.manual_page is None
assert shortcut.configure_url is None
assert shortcut.clients is None
assert shortcut.tags is None
assert not shortcut.login_required
assert shortcut.allowed_groups is None
assert Shortcut._all_shortcuts['test-component'] == shortcut
@ -42,19 +42,19 @@ def test_shortcut_init():
"""Test initializing shortcut component."""
clients = ['client1', 'client2']
allowed_groups = ['group1', 'group2']
shortcut = Shortcut('test-component', name='test-name',
short_description='test-short-description',
url='test-url', icon='test-icon',
description='test-description', manual_page='TestPage',
shortcut = Shortcut('test-component', name='test-name', url='test-url',
icon='test-icon', description='test-description',
manual_page='TestPage',
configure_url='test-configure-url', clients=clients,
login_required=True, allowed_groups=allowed_groups)
assert shortcut.short_description == 'test-short-description'
tags=['tag1', 'tag2'], login_required=True,
allowed_groups=allowed_groups)
assert shortcut.url == 'test-url'
assert shortcut.icon == 'test-icon'
assert shortcut.description == 'test-description'
assert shortcut.manual_page == 'TestPage'
assert shortcut.configure_url == 'test-configure-url'
assert shortcut.clients == clients
assert shortcut.tags == ['tag1', 'tag2']
assert shortcut.login_required
assert shortcut.allowed_groups == set(allowed_groups)
@ -91,9 +91,6 @@ def test_shortcut_list_sorting(common_shortcuts):
return_list = Shortcut.list(sort_by='name')
assert return_list == [cuts[0], cuts[1], cuts[2], cuts[3]]
return_list = Shortcut.list(sort_by='short_description')
assert return_list == [cuts[3], cuts[2], cuts[1], cuts[0]]
def test_shortcut_list_web_apps_only(common_shortcuts):
"""Test listing only web app shortcuts."""

View File

@ -27,8 +27,8 @@ def build_menu(size=5):
kwargs = {
'component_id': f'menu-test-{index}',
'name': f'Name{index}',
'short_description': f'ShortDescription{index}',
'icon': f'Icon{index}',
'tags': ['tag1', 'tag2'],
'url_name': f'test{index}',
'url_kwargs': {
'a': index,
@ -73,8 +73,8 @@ def test_menu_creation_without_arguments():
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.tags is None
assert menu.url == '/'
assert menu.order == 50
assert not menu.advanced
@ -84,23 +84,23 @@ def test_menu_creation_without_arguments():
def test_menu_creation_with_arguments():
"""Verify the Menu state with initialization parameters."""
expected_name = 'Name'
expected_short_description = 'ShortDescription'
expected_icon = 'Icon'
expected_tags = ['tag1', 'tag2']
url_name = 'test'
url_kwargs = {'a': 1, 'b': 2, 'c': 3}
expected_url = reverse(url_name, kwargs=url_kwargs)
expected_order = 42
parent_menu = Menu('menu-index', url_name='index')
menu = Menu('menu-test', expected_name, expected_short_description,
expected_icon, url_name, url_kwargs=url_kwargs,
parent_url_name='index', order=expected_order, advanced=True)
menu = Menu('menu-test', expected_name, expected_icon, expected_tags,
url_name, url_kwargs=url_kwargs, parent_url_name='index',
order=expected_order, advanced=True)
assert menu.parent_url_name == 'index'
assert len(parent_menu.items) == 1
assert parent_menu.items[0] == menu
assert expected_name == menu.name
assert expected_short_description == menu.short_description
assert expected_icon == menu.icon
assert expected_tags == menu.tags
assert expected_url == menu.url
assert expected_order == menu.order
assert menu.advanced

View File

@ -68,7 +68,7 @@ def get_breadcrumbs(request: HttpRequest) -> dict[str, dict[str, str | bool]]:
"""Return all the URL ancestors that can be show as breadcrumbs."""
breadcrumbs = {}
def _add(url: str, name: str, url_name: str | None = None):
def _add(url: str, name: str | None, url_name: str | None = None):
"""Add item into the breadcrumb dictionary."""
breadcrumbs[url] = {
'name': name,