doc: dev: Update the tutorial to reflect latest API/code

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: Veiko Aasa <veiko17@disroot.org>
This commit is contained in:
Sunil Mohan Adapa 2020-12-30 18:21:02 -08:00 committed by Veiko Aasa
parent 838a2ede8c
commit b378305f58
No known key found for this signature in database
GPG Key ID: 478539CAE680674E
8 changed files with 121 additions and 122 deletions

View File

@ -26,7 +26,7 @@ External References
#. `systemd System and Service Manager <https://www.freedesktop.org/wiki/Software/systemd/>`_
#. `Bootstrap - CSS Library <http://getbootstrap.com/css/>`_
#. `Bootstrap - CSS Library <http://getbootstrap.com/>`_
#. `FreedomBox User Manual <https://wiki.debian.org/FreedomBox/Manual>`_

View File

@ -6,17 +6,51 @@ App Module
These methods are optionally provided by the module in which an app is
implemented and FreedomBox calls/uses them if they are present.
<app-module>.init()
^^^^^^^^^^^^^^^^^^^
Optional. This method is called by FreedomBox soon after all the applications
are loaded. The ``init()`` call order guarantees that other applications that
this application depends on will be initialized before this application is
initialized.
<app-module>.depends
^^^^^^^^^^^^^^^^^^^^
Optional. This module property must contain a list of all apps that this
application depends on. The application is specified as string containing the
full module load path. For example, ``names``.
application depends on. The application is specified as string which is the
final part of the full module load path. For example, ``names``. Dependencies
are part of the :class:`~plinth.app.Info` component. Need for this attribute at
the module level will be removed in the future.
<app-module>.is_essential
^^^^^^^^^^^^^^^^^^^^^^^^^
Optional. If an app must be installed and configured by FreedomBox without user
intervention, this attribute must be set to True. This attribute is part of the
:class:`~plinth.app.Info` component. Need for this attribute at the module level
will be removed in the future.
<app-module>.version
^^^^^^^^^^^^^^^^^^^^
Optional. Version number of an app. Increasing the version number of an app
triggers the setup() logic allowing the app to run upgrade scripts. This
attribute is part of the :class:`~plinth.app.Info` component. Need for this
attribute at the module level will be removed in the future.
<app-module>.managed_packages
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Optional. This must contain the list of all packages that this app deals with.
This is mostly needed to enforce better security. This information may be moved
to a separate component in the future.
<app-module>.managed_services
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Optional. This must contain the list of all services that this app deals with.
This is mostly needed to enforce better security. This information is part of
the :class:`~plinth.daemon.Daemon` component. Need for this attribute at the
module level will be removed in the future.
<app-module>.managed_paths
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Optional. This must contain the list of all file system paths that this app
deals with. This is mostly used by the
:class:`~plinth.modules.letsencrypt.components.LetsEncrypt` component to enforce
better security. This requirement may be removed in the future.

View File

@ -5,7 +5,9 @@ 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.
these components are instantiated and added to the app as children. The
:class:`~plinth.menu.Menu` object added in the previous step is one such
component.
Providing basic information about the app
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -17,6 +19,8 @@ function normally.
from plinth import app as app_module
from . import manifest
class TransmissionApp(app_module.App):
...
@ -28,15 +32,45 @@ function normally.
icon_filename='transmission',
short_description=_('BitTorrent Web Client'),
description=description,
manual_page='Transmission', clients=clients)
manual_page='Transmission',
clients=manifest.clients,
donation_url='https://transmissionbt.com/donate/')
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.
page. The donation_url encourages our users to contribute to upstream projects
in order ensure their long term sustainability. More information about the
parameters is available in :class:`~plinth.app.Info` class documentation.
The description of app should provide basic information on what the app is about
and how to use it. It is impractical, however, to explain everything about the
app in a few short paragraphs. So, we need to write a page about the app in the
FreedomBox manual. This page will be available to the users from within the
FreedomBox web interface. To make this happen, let us write a `manual page entry
<https://wiki.debian.org/FreedomBox/Manual/Transmission>`_ for our app in the
`FreedomBox Wiki <https://wiki.debian.org/FreedomBox/Manual>`_ and then provide
a link to it from app page.
It would be helpful to our users if we can show how they can use our app. If
there are desktop and mobile clients that can used to access our service, we
need to list them and present them. Let's add this information to
``manifest.py``.
.. code-block:: python3
clients = [{
'name': _('Transmission'),
'platforms': [{
'type': 'web',
'url': '/transmission'
}]
}]
Since our app is a simple web application with no clients needed, we just list
that.
Managing a daemon
^^^^^^^^^^^^^^^^^

View File

@ -64,18 +64,21 @@ provide options to the user. Add the following to ``forms.py``.
from django import forms
class TransmissionForm(forms.Form): # pylint: disable=W0232
class TransmissionForm(DirectorySelectForm): # pylint: disable=W0232
"""Transmission configuration form"""
download_dir = forms.CharField(
label='Download directory',
help_text='Directory where downloads are saved. If you change the '
'default directory, ensure that the new directory exists '
'and is writable by "debian-transmission" user.')
This creates a Django form that shows a single option to set the download
directory for our Transmission app. This is how a regular Django form is built.
See :doc:`Django Forms documentation <django:topics/forms/index>` for more
information.
def __init__(self, *args, **kw):
validator = DirectoryValidator(username=SYSTEM_USER,
check_creatable=True)
super(TransmissionForm,
self).__init__(title=_('Download directory'),
default='/var/lib/transmission-daemon/downloads',
validator=validator, *args, **kw)
This uses a utility provided by the framework and creates a Django form that
shows a single option to set the download directory for our Transmission app.
This is similar to how a regular Django form is built. See :doc:`Django Forms
documentation <django:topics/forms/index>` for more information.
.. tip: Too many options
@ -100,8 +103,9 @@ the user submits it. Let us implement that in ``views.py``.
from .forms import TransmissionForm
class TransmissionAppView(views.AppView):
...
"""Serve configuration page."""
form_class = TransmissionForm
app_id = 'transmission'
def get_initial(self):
"""Get the current settings from Transmission server."""
@ -109,22 +113,18 @@ the user submits it. Let us implement that in ``views.py``.
configuration = actions.superuser_run('transmission',
['get-configuration'])
configuration = json.loads(configuration)
status.update({
key.translate(str.maketrans({
'-': '_'
})): value
for key, value in configuration.items()
})
status['storage_path'] = configuration['download-dir']
status['hostname'] = socket.gethostname()
return status
def form_valid(self, form):
"""Apply the changes submitted in the form."""
old_status = form.initial
new_status = form.cleaned_data
if old_status['download_dir'] != new_status['download_dir']:
if old_status['storage_path'] != new_status['storage_path']:
new_configuration = {
'download-dir': new_status['download_dir'],
'download-dir': new_status['storage_path'],
}
actions.superuser_run('transmission', ['merge-configuration'],

View File

@ -32,9 +32,11 @@ Coding standards
For readability and easy collaboration it is important to follow common coding
standards. FreedomBox uses the Python coding standards and uses the ``pylint``
and ``flake8`` tools to check if the there are any violations. Run these tools
on our application and fix any errors and warnings. Better yet, integrate these
tools into your favorite IDE for on-the-fly checking.
and ``flake8`` tools to check if the there are any violations. ``yapf`` and
``isort`` tools are used to automatically format the code to ensure that all
developers produce similarly formatted code. Run these tools on our application
and fix any errors and warnings. Better yet, integrate these tools into your
favorite IDE for on-the-fly checking.
For the most part, the code we have written so far, is already compliant with
the coding standards. This includes variable/method naming, indentation,

View File

@ -3,67 +3,6 @@
Part 7: Other Changes
---------------------
Showing information about app clients
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
It would be helpful to our users if we can show how they can use our app. If
there are desktop and mobile clients that can used to access our service, we
need to list them and present them. Let's add this information to
``manifest.py``.
.. code-block:: python3
clients = [{
'name': _('Transmission'),
'platforms': [{
'type': 'web',
'url': '/transmission'
}]
}]
Since our app is a simple web application with no clients needed, we just list
that. We need to include this into the main app view. In ``__init__.py``, add:
.. code-block:: python3
from . import manifest
In ``views.py``, add:
.. code-block:: python3
from plinth.modules import transmission
class TransmissionAppView(views.AppView):
...
clients = transmission.clients
Writing a manual page
^^^^^^^^^^^^^^^^^^^^^
The description of app should provide basic information on what the app is about
and how to use it. It is impractical, however, to explain everything about the
app in a few short paragraphs. So, we need to write a page about the app in the
FreedomBox manual. This page will be available to the users from within the
FreedomBox web interface. To make this happen, let us write a `manual page entry
<https://wiki.debian.org/FreedomBox/Manual/Transmission>`_ for our app in the
`FreedomBox Wiki <https://wiki.debian.org/FreedomBox/Manual>`_ and then provide
a link to it from app page. In ``__init__.py``, add:
.. code-block:: python3
manual_page = 'Transmission'
Then, in ``views.py``, add:
.. code-block:: python3
from plinth.modules import transmission
class TransmissionAppView(views.AppView):
...
manual_page = transmission.manual_page
Creating diagnostics
^^^^^^^^^^^^^^^^^^^^
@ -139,16 +78,17 @@ the Django's localization methods to make that happen.
from django.utils.translation import ugettext_lazy as _
name = _('Transmission')
class TransmissionApp(app_module.App):
...
short_description = _('BitTorrent Web Client')
def __init__(self):
...
description = [
_('BitTorrent is a peer-to-peer file sharing protocol. '
'Transmission daemon handles Bitorrent file sharing. Note that '
'BitTorrent is not anonymous.'),
_('Access the web interface at <a href="/transmission">/transmission</a>.')
]
info = app_module.Info(...
name=_('Transmission'),
...
short_description=_('BitTorrent Web Client'),
...)
Notice that the app's name, description, etc. are wrapped in the ``_()`` method
call. This needs to be done for the rest of our app. We use the

View File

@ -67,7 +67,7 @@ Creating the App class
^^^^^^^^^^^^^^^^^^^^^^
In the FreedomBox framework, each app must be a class derived from the
:class:`plinth.app.App`. Let us to that in ``__init__.py``. We will fill up the
:class:`plinth.app.App`. Let us do that in ``__init__.py``. We will fill up the
class later.
.. code-block:: python3

View File

@ -36,26 +36,15 @@ a link in FreedomBox web interface. Let us add a link in the apps list. In
from plinth.menu import main_menu
name = 'Transmission'
short_description = 'BitTorrent Web Client'
description = [
'BitTorrent is a peer-to-peer file sharing protocol. '
'Transmission daemon handles Bitorrent file sharing. Note that '
'BitTorrent is not anonymous.',
'Access the web interface at <a href="/transmission">/transmission</a>.'
]
class TransmissionApp(app_module.App):
...
def __init__(self):
...
menu_item = menu.Menu('menu-transmission', name, short_description,
'transmission', 'transmission:index',
parent_url_name='apps')
menu_item = menu.Menu('menu-transmission', 'Transmission',
'BitTorrrent Web Client', 'transmission',
'transmission:index', parent_url_name='apps')
self.add(menu_item)
What this does is add a menu item component into the our app. In FreedomBox