diff --git a/doc/dev/index.rst b/doc/dev/index.rst index f9e5966c3..1b98e752e 100644 --- a/doc/dev/index.rst +++ b/doc/dev/index.rst @@ -26,7 +26,7 @@ External References #. `systemd System and Service Manager `_ -#. `Bootstrap - CSS Library `_ +#. `Bootstrap - CSS Library `_ #. `FreedomBox User Manual `_ diff --git a/doc/dev/reference/app_module.rst b/doc/dev/reference/app_module.rst index de7be76f5..502d7373c 100644 --- a/doc/dev/reference/app_module.rst +++ b/doc/dev/reference/app_module.rst @@ -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. -.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. - .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. + + +.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. + +.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. + +.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. + +.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. + +.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. diff --git a/doc/dev/tutorial/components.rst b/doc/dev/tutorial/components.rst index 7321d77aa..5a13a5bf7 100644 --- a/doc/dev/tutorial/components.rst +++ b/doc/dev/tutorial/components.rst @@ -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 +`_ for our app in the +`FreedomBox Wiki `_ 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 ^^^^^^^^^^^^^^^^^ diff --git a/doc/dev/tutorial/customizing.rst b/doc/dev/tutorial/customizing.rst index e68fa4f08..6b4c24f6f 100644 --- a/doc/dev/tutorial/customizing.rst +++ b/doc/dev/tutorial/customizing.rst @@ -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 ` 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 ` 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'], diff --git a/doc/dev/tutorial/finishing.rst b/doc/dev/tutorial/finishing.rst index e1f6fae14..02bbd3e0c 100644 --- a/doc/dev/tutorial/finishing.rst +++ b/doc/dev/tutorial/finishing.rst @@ -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, diff --git a/doc/dev/tutorial/other.rst b/doc/dev/tutorial/other.rst index d2d5ee81e..69ea99595 100644 --- a/doc/dev/tutorial/other.rst +++ b/doc/dev/tutorial/other.rst @@ -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 -`_ for our app in the -`FreedomBox Wiki `_ 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 /transmission.') - ] + 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 diff --git a/doc/dev/tutorial/skeleton.rst b/doc/dev/tutorial/skeleton.rst index 771507d0a..729f87348 100644 --- a/doc/dev/tutorial/skeleton.rst +++ b/doc/dev/tutorial/skeleton.rst @@ -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 diff --git a/doc/dev/tutorial/view.rst b/doc/dev/tutorial/view.rst index 1ad7a5ac6..181d442bb 100644 --- a/doc/dev/tutorial/view.rst +++ b/doc/dev/tutorial/view.rst @@ -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 /transmission.' - ] - 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