• July 28, 2026, 10:19 p.m.

    Misago 0.39 introduced a new plugin system that has two hook types:

    • filters: wrap existing functions and other filters
    • actions: a list of functions to run in order when an event occurs

    I am now considering a third type: class extensions

    Lets say you have a class based view that looks like this:

    class CategoryThreadListView(View):
        def get_context_data(self):
            return {}
    

    Currently, get_context_data method is wrapped by a plugin hook:

    class CategoryThreadListView(View):
        def get_context_data(self):
            return thread_list_view_get_context_data_hook(self._get_context_data_action, self.request, self.category)
    
        def _get_context_data_action(self, request, category):
            return {}
    

    So far so good, but this already has one limitation: filter functions implemented by plugins don't have access to class attributes and methods, only to what is passed to them via the plugin hook.

    There is also another problem: if multiple methods should be exposed to plugins, multiple plugin hooks have to be implemented. This is slow to implement and maintain.

    The solution is to allow plugins to extend entire classes with mixins:

    @extend(CategoryThreadListView)
    class PluginCategoryThreadListViewMixin:
        def get_context_data(self):
            context_data = super().get_context_data()
            context_data["top_components"].append({"template_name": "plugin/component.html"})
            return context_data
    

    This plugin system is already present in multiple other forum softwares like Invision Community or XenForo... and it was suggested for Misago in past 😅