app: Introduce Info component to store basic app information

Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
Sunil Mohan Adapa 2020-02-10 17:41:37 -08:00 committed by James Valleroy
parent 7b05d7433d
commit 72ce23e377
No known key found for this signature in database
GPG Key ID: 77C0C75E7B650808
5 changed files with 165 additions and 1 deletions

View File

@ -6,6 +6,7 @@ Components
.. toctree::
:caption: Available components:
info
menu
daemon
firewall

View File

@ -0,0 +1,7 @@
.. SPDX-License-Identifier: CC-BY-SA-4.0
Info
^^^^
.. autoclass:: plinth.app.Info
:members:

View File

@ -3,6 +3,41 @@
Part 4: Components
------------------
Each :class:`~plinth.app.App` contains various :class:`~plinth.app.Component`
components that each provide one small functionality needed by the app. Each of
these components are instantiated and added to the app as children.
Providing basic information about the app
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
We need to provide some basic information about the application for the app to
function normally.
.. code-block:: python3
from plinth import app as app_module
class TransmissionApp(app_module.App):
...
def __init__(self):
...
info = app_module.Info(app_id=self.app_id, version=1,
name=_('Transmission'),
icon_filename='transmission',
short_description=_('BitTorrent Web Client'),
description=description,
manual_page='Transmission', clients=clients)
self.add(info)
The first argument is app_id that is same as the ID for the app. The version is
the version number for this app that must be incremented whenever setup() method
needs to be called again. name, icon_filename, short_description, description,
manual_page and clients provide information that is shown on the app's main
page. More information the parameters is available in :class:`~plinth.app.Info`
class documentation.
Managing a daemon
^^^^^^^^^^^^^^^^^

View File

@ -243,3 +243,90 @@ class LeaderComponent(Component):
def is_enabled(self):
"""Return if the component is enabled."""
raise NotImplementedError
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):
"""Store the basic properties of an app as a component.
Each app must contain at least one component of this type to provide
basic information about the app such as it's version number.
Instead of polluting the list of properties of an app, this component
stores them separately. This component can also be safely passed around
to template etc. without exposing the methods of an app and without
creating unnecessarily cyclic dependencies.
'app_id' must be the unique ID of the app to which this information
belongs.
'version' is the monotonically increasing positive integer starting at
1. It represents the version number of the app. It is used by the setup
mechanism. When an app's version number is increased, the setup
mechanism assumes that the setup() method of the app needs to run
again. This is used to upgrade/change configuration/setup of a app
when a new version of the app is deployed on users' machine.
'is_essential' is a boolean that marks the app as mandatory for the
basic functions of the system. If True, this app will be installed and
setup during the first run of FreedomBox even before first setup wizard
is shown to the user.
'depends' is the list of other apps that this app depends on. Apps from
this list are guaranteed to be initialized before initializing the app
to which this component belongs.
'name' is the user visible name of this app. It is shown as the title
of the app in the list of apps and when viewing app details. It should
be a lazily translated Django string.
'icon' is the name of icon to use with this app from a predetermined
list of icons. This is currently an icon class name from the Fork
Awesome font. It is used when showing the app in the System section.
Each app typically has either an 'icon' or 'icon_filename' property
set.
'icon_filename' is the name of the icon file, without the suffix, to be
used with this app. A .svg file (used in the web interface) and a .png
file (currently used by Android App) must be provided by the app. It is
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
a paragraph on the page. It may contain HTML <a> tags to provide links
to external content.
'manual_page' is the optional name of the page for this app in the user
manual. If provided, a 'Learn more...' link appears in the app page for
this app.
'clients' is the list of applications that can be used with the
services provided by this app. This is used to suggest installation of
compatible clients on desktop, web and mobile. This is a list of
dictionaries who structure is documented in plinth.clients.
"""
self.component_id = app_id + '-info'
self.app_id = app_id
self.version = version
self.is_essential = is_essential
self.depends = depends or []
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

@ -23,7 +23,7 @@ from unittest.mock import patch
import pytest
from plinth.app import App, Component, FollowerComponent, LeaderComponent
from plinth.app import App, Component, FollowerComponent, Info, LeaderComponent
class AppTest(App):
@ -282,3 +282,37 @@ def test_leader_component_is_enabled():
component = LeaderComponent('test-leader-1')
with pytest.raises(NotImplementedError):
assert component.is_enabled()
def test_info_initialization_without_args():
"""Test initializing the Info component without arguments."""
info = Info('test-app', 3)
assert info.component_id == 'test-app-info'
assert info.app_id == 'test-app'
assert info.version == 3
assert not info.is_essential
assert info.depends == []
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
def test_info_initialization_with_args():
"""Test initializing the Info component with arguments."""
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=['test'])
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 == ['test']