mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-01-21 07:55:00 +00:00
- This is completely reworked but based on /Developer page in the FreedomBox Manual. - This documentation can be made available as static site on https://docs.freedombox.org and the /Developer page in the FreedomBox Manual can be dropped. Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
223 lines
7.7 KiB
ReStructuredText
223 lines
7.7 KiB
ReStructuredText
.. SPDX-License-Identifier: CC-BY-SA-4.0
|
|
|
|
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
|
|
|
|
from plinth.clients import validate
|
|
|
|
clients = validate([{
|
|
'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 .manifest import clients
|
|
|
|
clients = clients
|
|
|
|
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
|
|
|
|
Adding backup/restore functionality
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Each app in FreedomBox needs to provide the ability to backup its configuration
|
|
and data. Apart from providing durability to users' data, this allows the user
|
|
to migrate from one machine to another. FreedomBox framework provides a simple
|
|
declarative mechanism to allow the app to be backed up and restored. In
|
|
``manifest.py``, add:
|
|
|
|
.. code-block:: python3
|
|
|
|
from plinth.modules.backups.api import validate as validate_backup
|
|
|
|
backup = validate_backup({
|
|
'data': {
|
|
'directories': ['/var/lib/transmission-daemon/.config']
|
|
},
|
|
'secrets': {
|
|
'files': ['/etc/transmission-daemon/settings.json']
|
|
},
|
|
'services': ['transmission-daemon']
|
|
})
|
|
|
|
The data and secrets information specifies which list of files and directories
|
|
FreedomBox framework needs to backup. The list of services specifies which
|
|
daemons should be stopped during the backup process. In ``__init__.py``, add:
|
|
|
|
.. code-block:: python3
|
|
|
|
from .manifest import backup
|
|
|
|
Creating diagnostics
|
|
^^^^^^^^^^^^^^^^^^^^
|
|
|
|
When the app does not work as expected, the user should known what is happening
|
|
with the app. The FreedomBox framework provides an API for running and showing
|
|
diagnostics results. The app has to implement a method for actually running the
|
|
diagnostics and return the results as a list. FreedomBox then takes care of
|
|
calling the diagnostics method and displaying the list in a formatted manner.
|
|
|
|
To implement the diagnostics, a method called ``diagnose()`` has to be available
|
|
as ``<app-module>.diagnose()``. It must return a list in which each item is the
|
|
result of a test performed. The item itself is a two-tuple containing the
|
|
display name of the test followed by the result as ``passed``, ``failed`` or
|
|
``error``.
|
|
|
|
.. code-block:: python3
|
|
|
|
def diagnose():
|
|
"""Run diagnostics and return the results."""
|
|
results = []
|
|
|
|
results.extend(action_utils.diagnose_url_on_all(
|
|
'https://{host}/transmission', extra_options=['--no-check-certificate']))
|
|
|
|
return results
|
|
|
|
Now that we have implemented diagnostics, we also need to show a diagnostics
|
|
button in the App's page. Adding an attribute to the
|
|
:class:`~plinth.views.AppView` will take care of this.
|
|
|
|
.. code-block:: python3
|
|
|
|
class TransmissionView(views.AppView):
|
|
...
|
|
diagnostics_module_name = 'transmission'
|
|
|
|
There are several helpers available to implement some of the common diagnostic
|
|
tests. For our application we wish to implement a test to check whether the
|
|
``/transmission`` URL is accessible. Since this is a commonly performed test,
|
|
there is a helper method available and we have used it in the above code. The
|
|
``{host}`` tag replaced with various IP addresses, hostnames and domain names by
|
|
the helper to produce different kinds of URLs and they are all tested. Results
|
|
for all tests are returned which we then pass on to the framework.
|
|
|
|
The user can trigger the diagnostics test by going to **System -> Diagnostics**
|
|
page. This runs diagnostics for all the applications. Users can also run
|
|
diagnostics specifically for this app from the app's page. A diagnostics button
|
|
is shown by the `app.html` template automatically when
|
|
``diagnostics_module_name`` attribute is set in the app's ``AppView`` derived
|
|
from :obj:`plinth.views.AppView`.
|
|
|
|
.. code-block:: django
|
|
|
|
{% include "diagnostics_button.html" with module="ttrss" enabled=True %}
|
|
|
|
Logging
|
|
^^^^^^^
|
|
|
|
Sometimes we may feel the need to write some debug messages to the console and
|
|
system logs. Doing this in FreedomBox is just like doing this any other Python
|
|
application.
|
|
|
|
.. code-block:: python3
|
|
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
def example_method():
|
|
logger.debug('A debug level message')
|
|
|
|
logger.info('Showing application page - %s', request.method)
|
|
|
|
try:
|
|
something()
|
|
except Exception as exception:
|
|
# Print stack trace
|
|
logger.exception('Encountered an exception - %s', exception)
|
|
|
|
For more information see Python :doc:`logging framework <howto/logging>`
|
|
documentation.
|
|
|
|
Internationalization
|
|
^^^^^^^^^^^^^^^^^^^^
|
|
|
|
Every string message that is visible to the user must be localized to user's
|
|
native language. For this to happen, our app needs to be internationalized. This
|
|
requires marking the user visible messages for translation. FreedomBox apps use
|
|
the Django's localization methods to make that happen.
|
|
|
|
.. code-block:: python3
|
|
|
|
from django.utils.translation import ugettext_lazy as _
|
|
|
|
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>.')
|
|
]
|
|
|
|
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
|
|
:obj:`~django.utils.translation.ugettext_lazy` in some cases and we use the
|
|
regular :obj:`~django.utils.translation.ugettext` in other cases. This is
|
|
because in the second case the :obj:`~django.utils.translation.gettext` lookup
|
|
is made once and reused for every user looking at the interface. These users may
|
|
each have a different language set for their interface. Lookup made for one
|
|
language for a user should not be used for other users. The ``_lazy`` methods
|
|
provided by Django makes sure that the return value is an object that will
|
|
actually be converted to string at the final moment when the string is being
|
|
displayed. In the first case, the lookup is made and string is returned
|
|
immediately.
|
|
|
|
All of this is the usual way internationalization is done in Django. See
|
|
:doc:`Internationalization and localization <django:topics/i18n/index>`
|
|
documentation for more information.
|