Skip to content

Commit

Permalink
Document how to develop extensions. (#1204)
Browse files Browse the repository at this point in the history
  • Loading branch information
bartfeenstra authored Jan 20, 2024
1 parent b870962 commit 36a454e
Show file tree
Hide file tree
Showing 15 changed files with 184 additions and 53 deletions.
5 changes: 5 additions & 0 deletions betty/app/extension/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,11 @@ def disable_requirement(self) -> Requirement:

@classmethod
def assets_directory_path(cls) -> Path | None:
"""
Return the path on disk where the extension's assets are located.
This may be anywhere in your Python package.
"""
return None

@property
Expand Down
52 changes: 17 additions & 35 deletions documentation/development.rst
Original file line number Diff line number Diff line change
@@ -1,45 +1,27 @@
Development
===========

.. toctree::
:glob:
:hidden:
:maxdepth: 1
:titlesonly:

development/extension
development/translation
development/test

This section covers how to develop Betty itself.

.. note::
Read :doc:`/about/contributing` if you would like to share your improvements to Betty with the wider community.

Getting started
---------------
:doc:`Install Betty from source </installation/source>`.

Working on translations
-----------------------

Making changes to the translatable strings in the source code
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Run ``betty update-translations`` to update the translations files with the changes you made.

Adding translations for a language for which no translations exist yet
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Run ``betty init-translation $locale`` where ``$locale`` is an
`IETF BCP 47 language tag <https://tools.ietf.org/html/bcp47>`_.

Testing
-------
In any existing Python environment, run ``./bin/test``.

Environment variables
^^^^^^^^^^^^^^^^^^^^^

These impact the ``./bin/test`` command:

* ``BETTY_TEST_SKIP_SHELLCHECK=true``: Skip ShellCheck tests.
* ``BETTY_TEST_SKIP_FLAKE8=true``: Skip Flake8 tests.
* ``BETTY_TEST_SKIP_MYPY=true``: Skip mypy tests.
* ``BETTY_TEST_SKIP_STYLELINT=true``: Skip Stylelint tests.
* ``BETTY_TEST_SKIP_ESLINT=true``: Skip ESLint tests.
* ``BETTY_TEST_SKIP_BUSTED=true``: Skip the Busted test build.
* ``BETTY_TEST_SKIP_CYPRESS=true``: Skip Cypress tests.
* ``BETTY_TEST_SKIP_PYINSTALLER=true``: Skip the PyInstaller test build.

Fixing problems automatically
-----------------------------
In any existing Python environment, run ``./bin/fix``.
In this section
---------------
- :doc:`development/extension`
- :doc:`development/translation`
- :doc:`development/test`
113 changes: 113 additions & 0 deletions documentation/development/extension.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
Developing a Betty extension
============================

Extensions are Betty plugins that integrate deeply with the Betty application and have the most power
to change or add functionality to your sites.

Getting started
---------------

#. Determine where in your package you want the extension to be located. Its fully qualified name will be
used as its extension name, e.g. an extension ``my_package.my_module.MyExtension`` will be named
``"my_package.my_module.MyExtension"``. This name is also used to enable the extension in
:doc:`project configuration files </usage/project/configuration>`.

#. Create a new class that extends :py:class:`betty.app.extension.Extension`, for example:
.. code-block:: python
from betty.app.extension import Extension
class MyExtension(Extension):
pass
Congratulations! You have created your very first Betty extension. Keep reading to learn how to add
functionality.

Making your extension discoverable
----------------------------------
Making your extension discoverable means that Betty knows it's available and can present users with the option
to enable and configure your extension for their project.

Given an extension ``my_package.my_module.MyExtension``, add the following to your extension's Python package:

.. tabs::
.. tab:: pyproject.toml
.. code-block:: toml
[project.entry-points.'betty.extensions']
'my_package.my_module.MyExtension' = 'my_package.my_module.MyExtension'
.. tab:: setup.py
.. code-block:: python
SETUP = {
'entry_points': {
'betty.extensions': [
'my_package.my_module.MyExtension=my_package.my_module.MyExtension',
],
},
}
if __name__ == '__main__':
setup(**SETUP)
Asset management
----------------
Extensions can enable :doc:`asset management </usage/assets>` to provide translations, templates, and more, by overriding
:py:meth:`betty.app.extension.Extension.assets_directory_path` to return the path on disk where the extension's assets
are located. This may be anywhere in your Python package.

.. code-block:: python
from betty.app.extension import Extension
class MyExtension(Extension):
@classmethod
def assets_directory_path(cls) -> Path | None:
# A directory named "assets" in the same parent directory as the current Python file.
return Path(__file__).parent / 'assets'
Dependencies
------------
.. note::
Any dependencies on other Python packages must be declared by your extension's Python package.

Extensions have fine-grained control over which other extensions they require, and the order in
which they appear in the extension dependency tree:

:py:meth:`betty.app.extension.Extension.depends_on`
Declare required other extensions. This ensures those extensions are enabled and appear before
your extension in the extension dependency tree.
:py:meth:`betty.app.extension.Extension.comes_after`
Declare other extensions that are not required, but if they **are** enabled, then your extension
will appear after them in the extension dependency tree.
:py:meth:`betty.app.extension.Extension.comes_before`
Declare other extensions that are not required, but if they **are** enabled, then your extension
will appear before them in the extension dependency tree.

Dispatching
-----------
Extensions can handle dispatched events by extending from any of the following classes:

:py:class:`betty.app.extension.ConfigurableExtension`
Enable configuration management for the extension.
:py:class:`betty.app.extension.Theme`
Mark the extension as being a theme, e.g. an extension that determines the overall look and
feel of a site.
:py:class:`betty.app.extension.UserFacingExtension`
Mark the extension as being suitable for end user interaction, e.g. it is not internal.
:py:class:`betty.generate.Generator`
Dispatched when the site is being generated. This is used to tell extensions when to
generate their parts of the site.
:py:class:`betty.gui.GuiBuilder`
Provide a Graphical User Interface to manage the extension in the Betty Desktop application.
:py:class:`betty.html.CssProvider`
Add additional CSS files to generated pages.
:py:class:`betty.html.JsProvider`
Add additional JavaScript files to generated pages.
:py:class:`betty.jinja2.Jinja2Provider`
Integrate the extension with :doc:`Jinja2 </usage/templating>`.
:py:class:`betty.load.Loader`
Dispatched when data is loaded into an ancestry. This is used to import data.
:py:class:`betty.load.PostLoader`
Dispatched after data is loaded into an ancestry. This is used to modify loaded data.
22 changes: 22 additions & 0 deletions documentation/development/test.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Testing Betty's source code
===========================

In any existing Python environment, run ``./bin/test``.

Environment variables
---------------------

These impact the ``./bin/test`` command:

* ``BETTY_TEST_SKIP_SHELLCHECK=true``: Skip ShellCheck tests.
* ``BETTY_TEST_SKIP_FLAKE8=true``: Skip Flake8 tests.
* ``BETTY_TEST_SKIP_MYPY=true``: Skip mypy tests.
* ``BETTY_TEST_SKIP_STYLELINT=true``: Skip Stylelint tests.
* ``BETTY_TEST_SKIP_ESLINT=true``: Skip ESLint tests.
* ``BETTY_TEST_SKIP_BUSTED=true``: Skip the Busted test build.
* ``BETTY_TEST_SKIP_CYPRESS=true``: Skip Cypress tests.
* ``BETTY_TEST_SKIP_PYINSTALLER=true``: Skip the PyInstaller test build.

Fixing problems automatically
-----------------------------
In any existing Python environment, run ``./bin/fix``.
13 changes: 13 additions & 0 deletions documentation/development/translation.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Working on Betty's translations
===============================

Making changes to the translatable strings in the source code
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Run ``betty update-translations`` to update the translations files with the changes you made.

Adding translations for a language for which no translations exist yet
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Run ``betty init-translation $locale`` where ``$locale`` is an
`IETF BCP 47 language tag <https://tools.ietf.org/html/bcp47>`_.
14 changes: 5 additions & 9 deletions documentation/usage/extension.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,7 @@ Extensions
:maxdepth: 1
:titlesonly:

extension/cotton_candy
extension/deriver
extension/gramps
extension/http_api_doc
extension/maps
extension/nginx
extension/privatizer
extension/trees
extension/wikipedia
extension/*

Betty's functionality can be altered using *extensions*. An extension can do many things, such as loading new or
expanding existing ancestry data, or generating additional content for your site.
Expand All @@ -35,3 +27,7 @@ In this section
- :doc:`extension/privatizer`
- :doc:`extension/trees`
- :doc:`extension/wikipedia`

See also
--------
- :doc:`/development/extension`
2 changes: 1 addition & 1 deletion documentation/usage/extension/cotton_candy.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
The Cotton Candy extension
The *Cotton Candy* extension
==========================
The :py:class:`betty.extension.CottonCandy` extension provides Betty's default theme.

Expand Down
2 changes: 1 addition & 1 deletion documentation/usage/extension/deriver.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
The Deriver extension
The *Deriver* extension
=====================
The :py:class:`betty.extension.Deriver` extension derives, or infers, events for people based on their existing events. For example, we know that someone's
final disposition, such as a burial or cremation, comes after their death. If a person has a *burial* event without a
Expand Down
2 changes: 1 addition & 1 deletion documentation/usage/extension/gramps.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
The Gramps extension
The *Gramps* extension
====================
The :py:class:`betty.extension.Gramps` extension loads entities from `Gramps <https://gramps-project.org>`_ family trees into your Betty ancestry.

Expand Down
2 changes: 1 addition & 1 deletion documentation/usage/extension/http_api_doc.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
The HTTP API Documentation extension
The *HTTP API Documentation* extension
====================================
The :py:class:`betty.extension.HttpApiDoc` extension renders interactive and user-friendly HTTP API documentation using
`ReDoc <https://github.com/Redocly/redoc>`_.
Expand Down
2 changes: 1 addition & 1 deletion documentation/usage/extension/maps.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
The Maps extension
The *Maps* extension
==================
The :py:class:`betty.extension.Maps` extension renders interactive maps using `Leaflet <https://leafletjs.com/>`_ and
`OpenStreetMap <https://www.openstreetmap.org/>`_.
Expand Down
2 changes: 1 addition & 1 deletion documentation/usage/extension/nginx.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
The nginx extension
The *nginx* extension
===================
The :py:class:`betty.extension.Nginx` extension creates an `nginx <https://nginx.org>`_ configuration file and a `Docker <https://www.docker.com/>`_ ``Dockerfile`` in the output
directory.
Expand Down
2 changes: 1 addition & 1 deletion documentation/usage/extension/privatizer.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
The Privatizer extension
The *Privatizer* extension
========================
The :py:class:`betty.extension.Privatizer` extension marks entities :doc:`private </usage/model/ancestry/privacy>` to prevent sensitive information from being published accidentally. It is most
powerful in achieving this when used in combination with the :doc:`anonymizer <anonymizer>` and :doc:`cleaner <cleaner>`
Expand Down
2 changes: 1 addition & 1 deletion documentation/usage/extension/trees.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
The Trees extension
The *Trees* extension
===================
The :py:class:`betty.extension.Trees` extension renders interactive family trees using `Cytoscape.js <http://js.cytoscape.org/>`_.

Expand Down
2 changes: 1 addition & 1 deletion documentation/usage/extension/wikipedia.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
The Wikipedia extension
The *Wikipedia* extension
=======================
The :py:class:`betty.extension.Wikipedia` extension renders summaries of Wikipedia articles. If a entity such as a person or a place contains
links to Wikipedia articles, templates can use this extension to fetch translated summaries of these articles, and
Expand Down

0 comments on commit 36a454e

Please sign in to comment.