Skip to content

Signal list

This page lists the signals and hooks that are available in byro. The plugin guides give examples on how to use these signals.

Member management

byro.members.signals

new_member module-attribute

new_member = django.dispatch.Signal()

Receives the new member as signal. If an exception is raised, the error message will be displayed in the frontend as a warning.

new_member_mail_information module-attribute

new_member_mail_information = django.dispatch.Signal()

Receives the new member as signal. Response will be added to the email welcoming the new member.

new_member_office_mail_information module-attribute

new_member_office_mail_information = django.dispatch.Signal()

Receives the new member as signal. Response will be added to the email notifying the office about the new member.

leave_member module-attribute

leave_member = django.dispatch.Signal()

Receives the new member as signal. If an exception is raised, the error message will be displayed in the frontend as a warning.

leave_member_mail_information module-attribute

leave_member_mail_information = django.dispatch.Signal()

Receives the leaving member as signal. Response will be added to the email confirming termination to the member.

leave_member_office_mail_information module-attribute

leave_member_office_mail_information = django.dispatch.Signal()

Receives the leaving member as signal. Response will be added to the email notifying the office about the termination of the member.

update_member module-attribute

update_member = django.dispatch.Signal()

If a member is updated via the office form collection at members/view/{id}/data. The signal receives the request, and the form_list as parameters. The changes will already have been saved at this point.

Payment

byro.bookkeeping.signals

process_transaction module-attribute

process_transaction = django.dispatch.Signal()

This signal provides a Transaction as sender and expects the receiver to augment the Transaction with auto-detected information as appropriate.

The common case is a Transaction that is unbalanced and can be augmented to be a balanced Transaction by adding one or more Bookings.

Recipients MUST NOT change any data in the Transaction or its Bookings if Transaction.is_read_only is True.

bank_transaction_importers module-attribute

bank_transaction_importers = django.dispatch.Signal()

This signal allows you to register bank transaction importers that appear in the importer selection of the "Import bank transactions" page. Receives None as sender. Must return a single importer object or an iterable of importer objects. An importer provides a stable identifier, a translatable label and a parse(source) method that yields :class:byro.bookkeeping.bank_import.ImportedBankTransaction objects for the given :class:~byro.bookkeeping.models.RealTransactionSource::

@receiver(bank_transaction_importers)
def register_importer(sender, **kwargs):
    return ExampleBankImporter()

See :doc:/developer/plugins/bank-transaction-importers for the complete contract. Importers must not create Transaction or Booking objects themselves; byro's core persists the yielded transactions, detects duplicates and runs the process_transaction matching afterwards.

process_csv_upload module-attribute

process_csv_upload = django.dispatch.Signal()

Legacy API, deprecated for new development. Use bank_transaction_importers instead.

This signal provides a RealTransactionSource as sender and expects a list of one or more Transactions in response. It is only sent for sources that were not assigned a bank transaction importer (RealTransactionSource.importer is empty), and exactly one receiver must be connected.

If the RealTransactionSource has already been processed, no Transactions should be created, unless you are very sure what you are doing.

process_csv_upload is the legacy signal, see Bank transaction importers. Bank transaction importers are registered via bank_transaction_importers (see Bank transaction importers).

Display and import

byro.office.signals

nav_event module-attribute

nav_event = django.dispatch.Signal()

This signal allows you to add additional views to the sidebar. Receives the request as sender. Must return a dictionary containing at least the keys label and url. You can also return a ForkAwesome icon name with the key icon. You should also return an active key with a boolean set to True if this item should be marked as active.

If you want your Plugin to appear in the "Finance" or "Settings" submenu in the side bar, please set section in your return dict to either finance or settings, and don't set an icon.

The "Settings" submenu is only shown to superusers. An entry with section set to settings therefore marks an administrative function, and the views behind it must enforce that themselves with byro.common.permissions.SuperuserRequiredMixin (or the superuser_required decorator). Hiding the navigation entry is not an access control: byro does not know which views belong to your entry and cannot protect them for you.

May return an iterable of multiple dictionaries as described above.

member_view module-attribute

member_view = django.dispatch.Signal()

This signal allows you to add a tab to the member detail view tab list. Receives the member as sender, and additionally the request Must return a dict::

{
    "label": _("Fancy Member View"),
    "url": "/member/123/foo/",
    "url_name": "plugins:myplugin:foo_view",
}

Please use byro.office.views.members.MemberView as base class for these views.

member_dashboard_tile module-attribute

member_dashboard_tile = django.dispatch.Signal()

This signal allows you to add tiles to the member's dashboard. Receives None as argument, must return either None or a dict::

{
    "title": _("Dash!"),
    "lines": [_('Line 1'), _('Line 2')]
    "url": "/member/123/foo/",
    "public": False,  # False is the default
}

All of the parts of this dict are optional. You cannot include HTML in the response, all strings will be escaped at render time. If "public" is set to True, the dashboard tile will also be shown on the member's personal page.

member_list_importers module-attribute

member_list_importers = django.dispatch.Signal()

This signal allows you to add additional member list importers. Receives None as argument, must return a dict::

{
    "id": "dot.scoped.importer.id",
    "label": _("My super importer"),
    "form_valid": form_valid_callback,
}

where form_valid_callback should accept two arguments: view (the View object handling the request), and form (the form object that was submitted, the file to import is in the upload_file form field) and should return a Response object.

Settings entries are superuser only

The "Settings" submenu of the sidebar is only shown to superusers (see Permission model). A nav_event entry with section set to settings therefore marks an administrative function that is meant for superusers only.

Hiding the navigation entry is not an access control. byro cannot tell which views belong to a navigation entry and does not try to block plugin URLs on its own. A plugin has to protect every view behind such an entry itself, on the server:

from django.views.generic import FormView

from byro.common.permissions import SuperuserRequiredMixin, superuser_required


class NewsletterSettingsView(SuperuserRequiredMixin, FormView):
    ...


@superuser_required
def newsletter_settings_export(request):
    ...

Both checks are self-contained: they require an authenticated, active superuser and do not depend on byro's middleware. Anonymous visitors are redirected to the login page, every other account gets byro's "Permission denied" page (HTTP 403). In templates, use request.user.is_superuser to decide whether to show a link to such a page. byro.common.permissions.has_backend_access(user) is the central definition of who may use the Office at all (staff or superuser).

Configuration models that inherit from ByroConfiguration need no extra work: they are part of the general settings page, which is restricted to superusers already. Entries with section set to finance, or without a section, stay visible to every account with access to the Office.

Migration note for existing plugins

Before the permission model was introduced, every account could open every page, so existing plugins usually contain no such check. Their "Settings" entries are hidden from staff now, but the pages behind them stay reachable for staff through their direct address until the plugin protects them as shown above. This is a compatibility limit for existing third-party plugins, not the intended permission behavior.

If you maintain a plugin with entries in the "Settings" section: add the mixin or the decorator to every view behind those entries, including views that only handle form submissions or exports. byro.common.permissions is available from the byro release that introduces the permission model; a plugin that also has to run on older releases can check request.user.is_superuser itself and raise django.core.exceptions.PermissionDenied.

General

Not generated

byro.common, unlike every other byro app, has no __init__.py (see src/byro/common/). mkdocstrings/Griffe therefore cannot statically collect byro.common.signals; the descriptions below are copied from its source by hand. This is reported as a product finding, not fixed here (the documentation does not change product code).

Module byro.common.signals:

unauthenticated_urls
Sent to determine whether a URL should be reachable without authentication. Plugins connect a receiver to mark their own views public; the MFA and permission middleware exempt matching URLs.
log_formatters
Sent to collect formatters for audit log entries, so plugins can render their own log actions in the office UI.
periodic_task
Sent by byroctl manage runperiodic / python -m byro runperiodic. Connect a receiver to run recurring work, for example the built-in PGP key refresh and expiry reminders.