diff --git a/docs/concepts/extension_points.rst b/docs/concepts/extension_points.rst index d4e802baec0e..3136aa8057c2 100644 --- a/docs/concepts/extension_points.rst +++ b/docs/concepts/extension_points.rst @@ -139,10 +139,10 @@ Here are the different integration points that python plugins can use: - This decorator allows overriding any function or method by pointing to an alternative implementation in settings. Read the |pluggable_override docstring|_ to learn more. * - Open edX Events - Adopt, Stable - - Events are part of the greater Hooks Extension Framework for open extension of edx-platform. Events are a stable way for plugin developers to react to learner or author events. They are defined by a `separate events library`_ that developers can include in their requirements to develop and test the code without creating a dependency on this large repo. For more information see the `hooks guide`_. + - Events are part of the greater Hooks Extension Framework for open extension of edx-platform. Events are a stable way for plugin developers to react to learner or author events. They are defined by a `separate events library`_ that developers can include in their requirements to develop and test the code without creating a dependency on this large repo. For more information see the `Hooks Extension Framework docs`_ or for more detailed documentation about Open edX Events, see the `Open edX Events documentation`_. * - Open edX Filters - Adopt, Stable - - Filters are also part of Hooks Extension Framework for open extension of edx-platform. Filters are a flexible way for plugin developers to modify learner or author application flows. They are defined by a `separate filters library`_ that developers can include in their requirements to develop and test the code without creating a dependency on this large repo. For more information see the `hooks guide`_. + - Filters are also part of Hooks Extension Framework for open extension of edx-platform. Filters are a flexible way for plugin developers to modify learner or author application flows. They are defined by a `separate filters library`_ that developers can include in their requirements to develop and test the code without creating a dependency on this large repo. For more information see the `Hooks Extension Framework docs`_ or for more detailed documentation about Open edX Filters, see the `Open edX Filters documentation`_. .. _Application: https://docs.djangoproject.com/en/3.0/ref/applications/ .. _Django app plugin documentation: https://github.com/openedx/edx-platform/blob/master/openedx/core/djangoapps/plugins/README.rst @@ -159,7 +159,9 @@ Here are the different integration points that python plugins can use: .. _pluggable_override docstring: https://github.com/openedx/edx-django-utils/blob/master/edx_django_utils/plugins/pluggable_override.py .. _separate events library: https://github.com/eduNEXT/openedx-events/ .. _separate filters library: https://github.com/eduNEXT/openedx-filters/ -.. _hooks guide: https://github.com/openedx/edx-platform/blob/master/docs/guides/hooks/index.rst +.. _Hooks Extension Framework docs: https://docs.openedx.org/en/latest/developers/concepts/hooks_extension_framework.html +.. _Open edX Events documentation: https://docs.openedx.org/projects/openedx-events/en/latest/ +.. _Open edX Filters documentation: https://docs.openedx.org/projects/openedx-filters/en/latest/ Platform Look & Feel ==================== diff --git a/docs/conf.py b/docs/conf.py index b755f3986c93..ec416f1c19e6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -68,6 +68,7 @@ 'code_annotations.contrib.sphinx.extensions.featuretoggles', 'code_annotations.contrib.sphinx.extensions.settings', # 'autoapi.extension', # Temporarily disabled + 'sphinx_reredirects', ] # Temporarily disabling autoapi_dirs and the AutoAPI extension due to performance issues. @@ -304,6 +305,16 @@ # 'xmodule': 'references/docstrings/xmodule', } +# Mapping permanently moved pages to appropriate new location outside of edx-platform +# with by sphinx-reredirects extension redirects. +# More information: https://documatt.com/sphinx-reredirects/usage.html + +redirects = { + 'hooks/events': 'https://docs.openedx.org/projects/openedx-events/en/latest/', + 'hooks/filters': 'https://docs.openedx.org/projects/openedx-filters/en/latest/', + 'hooks/index': 'https://docs.openedx.org/en/latest/developers/concepts/hooks_extension_framework.html', +} + def update_settings_module(service='lms'): """ diff --git a/docs/hooks/events.rst b/docs/hooks/events.rst deleted file mode 100644 index bccb98e56a42..000000000000 --- a/docs/hooks/events.rst +++ /dev/null @@ -1,261 +0,0 @@ -Open edX Events -=============== - -How to use ----------- - -Using openedx-events in your code is very straight forward. We can consider the -two possible cases, sending or receiving an event. - - -Receiving events -^^^^^^^^^^^^^^^^ - -This is one of the most common use cases for plugins. The edx-platform will send -an event and you want to react to it in your plugin. - -For this you need to: - -1. Include openedx-events in your dependencies. -2. Connect your receiver functions to the signals being sent. - -Connecting signals can be done using regular django syntax: - -.. code-block:: python - - from openedx_events.learning.signals import SESSION_LOGIN_COMPLETED - - @receiver(SESSION_LOGIN_COMPLETED) - # your receiver function here - - -Or at the apps.py - -.. code-block:: python - - "signals_config": { - "lms.djangoapp": { - "relative_path": "your_module_name", - "receivers": [ - { - "receiver_func_name": "your_receiver_function", - "signal_path": "openedx_events.learning.signals.SESSION_LOGIN_COMPLETED", - }, - ], - } - }, - -In case you are listening to an event in the edx-platform repo, you can directly -use the django syntax since the apps.py method will not be available without the -plugin. - - -Sending events -^^^^^^^^^^^^^^ - -Sending events requires you to import both the event definition as well as the -attr data classes that encapsulate the event data. - -.. code-block:: python - - from openedx_events.learning.data import UserData, UserPersonalData - from openedx_events.learning.signals import STUDENT_REGISTRATION_COMPLETED - - STUDENT_REGISTRATION_COMPLETED.send_event( - user=UserData( - pii=UserPersonalData( - username=user.username, - email=user.email, - name=user.profile.name, - ), - id=user.id, - is_active=user.is_active, - ), - ) - -You can do this both from the edx-platform code as well as from an openedx -plugin. - - -Testing events -^^^^^^^^^^^^^^ - -Testing your code in CI, specially for plugins is now possible without having to -import the complete edx-platform as a dependency. - -To test your functions you need to include the openedx-events library in your -testing dependencies and make the signal connection in your test case. - -.. code-block:: python - - from openedx_events.learning.signals import STUDENT_REGISTRATION_COMPLETED - - def test_your_receiver(self): - STUDENT_REGISTRATION_COMPLETED.connect(your_function) - STUDENT_REGISTRATION_COMPLETED.send_event( - user=UserData( - pii=UserPersonalData( - username='test_username', - email='test_email@example.com', - name='test_name', - ), - id=1, - is_active=True, - ), - ) - - # run your assertions - - -Changes in the openedx-events library that are not compatible with your code -should break this kind of test in CI and let you know you need to upgrade your -code. - - -Live example -^^^^^^^^^^^^ - -For a complete and detailed example you can see the `openedx-events-2-zapier`_ -plugin. This is a fully functional plugin that connects to -``STUDENT_REGISTRATION_COMPLETED`` and ``COURSE_ENROLLMENT_CREATED`` and sends -the relevant information to zapier.com using a webhook. - -.. _openedx-events-2-zapier: https://github.com/eduNEXT/openedx-events-2-zapier - - -Index of Events ------------------ - -This list contains the events currently being sent by edx-platform. The provided -links target both the definition of the event in the openedx-events library as -well as the trigger location in this same repository. - - -Learning Events -^^^^^^^^^^^^^^^ - -.. list-table:: - :widths: 35 50 20 - - * - *Name* - - *Type* - - *Date added* - - * - `STUDENT_REGISTRATION_COMPLETED `_ - - org.openedx.learning.student.registration.completed.v1 - - `2022-06-14 `_ - - * - `SESSION_LOGIN_COMPLETED `_ - - org.openedx.learning.auth.session.login.completed.v1 - - `2022-06-14 `_ - - * - `COURSE_ENROLLMENT_CREATED `_ - - org.openedx.learning.course.enrollment.created.v1 - - `2022-06-14 `_ - - * - `COURSE_ENROLLMENT_CHANGED `_ - - org.openedx.learning.course.enrollment.changed.v1 - - `2022-06-14 `_ - - * - `COURSE_UNENROLLMENT_COMPLETED `_ - - org.openedx.learning.course.unenrollment.completed.v1 - - `2022-06-14 `_ - - * - `CERTIFICATE_CREATED `_ - - org.openedx.learning.certificate.created.v1 - - `2022-06-14 `_ - - * - `CERTIFICATE_CHANGED `_ - - org.openedx.learning.certificate.changed.v1 - - `2022-06-14 `_ - - * - `CERTIFICATE_REVOKED `_ - - org.openedx.learning.certificate.revoked.v1 - - `2022-06-14 `_ - - * - `COHORT_MEMBERSHIP_CHANGED `_ - - org.openedx.learning.cohort_membership.changed.v1 - - `2022-06-14 `_ - - * - `COURSE_DISCUSSIONS_CHANGED `_ - - org.openedx.learning.discussions.configuration.changed.v1 - - `2022-06-14 `_ - - -Content Authoring Events -^^^^^^^^^^^^^^^^^^^^^^^^ - -.. list-table:: - :widths: 35 50 20 - - * - *Name* - - *Type* - - *Date added* - - * - `COURSE_CATALOG_INFO_CHANGED `_ - - org.openedx.content_authoring.course.catalog_info.changed.v1 - - `2022-08-24 `_ - - * - `XBLOCK_PUBLISHED `_ - - org.openedx.content_authoring.xblock.published.v1 - - `2022-12-06 `_ - - * - `XBLOCK_DELETED `_ - - org.openedx.content_authoring.xblock.deleted.v1 - - `2022-12-06 `_ - - * - `XBLOCK_DUPLICATED `_ - - org.openedx.content_authoring.xblock.duplicated.v1 - - `2022-12-06 `_ - - * - `XBLOCK_CREATED `_ - - org.openedx.content_authoring.xblock.created.v1 - - 2023-07-20 - - * - `XBLOCK_UPDATED `_ - - org.openedx.content_authoring.xblock.updated.v1 - - 2023-07-20 - - * - `COURSE_CREATED `_ - - org.openedx.content_authoring.course.created.v1 - - 2023-07-20 - - * - `CONTENT_LIBRARY_CREATED `_ - - org.openedx.content_authoring.content_library.created.v1 - - 2023-07-20 - - * - `CONTENT_LIBRARY_UPDATED `_ - - org.openedx.content_authoring.content_library.updated.v1 - - 2023-07-20 - - * - `CONTENT_LIBRARY_DELETED `_ - - org.openedx.content_authoring.content_library.deleted.v1 - - 2023-07-20 - - * - `LIBRARY_BLOCK_CREATED `_ - - org.openedx.content_authoring.library_block.created.v1 - - 2023-07-20 - - * - `LIBRARY_BLOCK_UPDATED `_ - - org.openedx.content_authoring.library_block.updated.v1 - - 2023-07-20 - - * - `LIBRARY_BLOCK_DELETED `_ - - org.openedx.content_authoring.library_block.deleted.v1 - - 2023-07-20 - - * - `LIBRARY_COLLECTION_CREATED `_ - - org.openedx.content_authoring.content_library.collection.created.v1 - - 2024-08-23 - - * - `LIBRARY_COLLECTION_UPDATED `_ - - org.openedx.content_authoring.content_library.collection.updated.v1 - - 2024-08-23 - - * - `LIBRARY_COLLECTION_DELETED `_ - - org.openedx.content_authoring.content_library.collection.deleted.v1 - - 2024-08-23 - - * - `CONTENT_OBJECT_ASSOCIATIONS_CHANGED `_ - - org.openedx.content_authoring.content.object.associations.changed.v1 - - 2024-09-06 diff --git a/docs/hooks/filters.rst b/docs/hooks/filters.rst deleted file mode 100644 index b2ce68fc147d..000000000000 --- a/docs/hooks/filters.rst +++ /dev/null @@ -1,191 +0,0 @@ -Open edX Filters -================ - -How to use ----------- - -Using openedx-filters in your code is very straight forward. We can consider the -two possible cases: - -Configuring a filter -^^^^^^^^^^^^^^^^^^^^ - -Implement pipeline steps -************************ - -Let's say you want to consult student's information with a third party service -before generating the students certificate. This is a common use case for filters, -where the functions part of the filter's pipeline will perform the consulting tasks and -decide the execution flow for the application. These functions are the pipeline steps, -and can be implemented in an installable Python library: - -.. code-block:: python - - # Step implementation taken from openedx-filters-samples plugin - from openedx_filters import PipelineStep - from openedx_filters.learning.filters import CertificateCreationRequested - - class StopCertificateCreation(PipelineStep): - - def run_filter(self, user, course_id, mode, status): - # Consult third party service and check if continue - # ... - # User not in third party service, denied certificate generation - raise CertificateCreationRequested.PreventCertificateCreation( - "You can't generate a certificate from this site." - ) - -There's two key components to the implementation: - -1. The filter step must be a subclass of ``PipelineStep``. - -2. The ``run_filter`` signature must match the filters definition, eg., -the previous step matches the method's definition in CertificateCreationRequested. - -Attach/hook pipeline to filter -****************************** - -After implementing the pipeline steps, we have to tell the certificate creation -filter to execute our pipeline. - -.. code-block:: python - - OPEN_EDX_FILTERS_CONFIG = { - "org.openedx.learning.certificate.creation.requested.v1": { - "fail_silently": False, - "pipeline": [ - "openedx_filters_samples.samples.pipeline.StopCertificateCreation" - ] - }, - } - -Triggering a filter -^^^^^^^^^^^^^^^^^^^ - -In order to execute a filter in your own plugin/library, you must install the -plugin where the steps are implemented and also, ``openedx-filters``. - -.. code-block:: python - - # Code taken from lms/djangoapps/certificates/generation_handler.py - from openedx_filters.learning.filters import CertificateCreationRequested - - try: - self.user, self.course_id, self.mode, self.status = CertificateCreationRequested.run_filter( - user=self.user, course_id=self.course_id, mode=self.mode, status=self.status, - ) - except CertificateCreationRequested.PreventCertificateCreation as exc: - raise CertificateGenerationNotAllowed(str(exc)) from exc - -Testing filters' steps -^^^^^^^^^^^^^^^^^^^^^^ - -It's pretty straightforward to test your pipeline steps, you'll need to include the -``openedx-filters`` library in your testing dependencies and configure them in your test case. - -.. code-block:: python - - from openedx_filters.learning.filters import CertificateCreationRequested - - @override_settings( - OPEN_EDX_FILTERS_CONFIG={ - "org.openedx.learning.certificate.creation.requested.v1": { - "fail_silently": False, - "pipeline": [ - "openedx_filters_samples.samples.pipeline.StopCertificateCreation" - ] - } - } - ) - def test_certificate_creation_requested_filter(self): - """ - Test filter triggered before the certificate creation process starts. - - Expected results: - - The pipeline step configured for the filter raises PreventCertificateCreation - when the conditions are met. - """ - with self.assertRaises(CertificateCreationRequested.PreventCertificateCreation): - CertificateCreationRequested.run_filter( - user=self.user, course_key=self.course_key, mode="audit", - ) - - # run your assertions - -Changes in the ``openedx-filters`` library that are not compatible with your code -should break this kind of test in CI and let you know you need to upgrade your code. -The main limitation while testing filters' steps it's their arguments, as they are edxapp -memory objects, but that can be solved in CI using Python mocks. - -Live example -^^^^^^^^^^^^ - -For filter steps samples you can visit the `openedx-filters-samples`_ plugin, where -you can find minimal steps exemplifying the different ways on how to use -``openedx-filters``. - -.. _openedx-filters-samples: https://github.com/eduNEXT/openedx-filters-samples - - -Index of Filters ------------------ - -This list contains the filters currently being executed by edx-platform. The provided -links target both the definition of the filter in the openedx-filters library as -well as the trigger location in this same repository. - - -.. list-table:: - :widths: 35 50 20 - - * - *Name* - - *Type* - - *Date added* - - * - `StudentRegistrationRequested `_ - - org.openedx.learning.student.registration.requested.v1 - - `2022-06-14 `_ - - * - `StudentLoginRequested `_ - - org.openedx.learning.student.login.requested.v1 - - `2022-06-14 `_ - - * - `CourseEnrollmentStarted `_ - - org.openedx.learning.course.enrollment.started.v1 - - `2022-06-14 `_ - - * - `CourseUnenrollmentStarted `_ - - org.openedx.learning.course.unenrollment.started.v1 - - `2022-06-14 `_ - - * - `CertificateCreationRequested `_ - - org.openedx.learning.certificate.creation.requested.v1 - - `2022-06-14 `_ - - * - `CertificateRenderStarted `_ - - org.openedx.learning.certificate.render.started.v1 - - `2022-06-14 `_ - - * - `CohortChangeRequested `_ - - org.openedx.learning.cohort.change.requested.v1 - - `2022-06-14 `_ - - * - `CohortAssignmentRequested `_ - - org.openedx.learning.cohort.assignment.requested.v1 - - `2022-06-14 `_ - - * - `CourseAboutRenderStarted `_ - - org.openedx.learning.course_about.render.started.v1 - - `2022-06-14 `_ - - * - `DashboardRenderStarted `_ - - org.openedx.learning.dashboard.render.started.v1 - - `2022-06-14 `_ - - * - `VerticalBlockChildRenderStarted `_ - - org.openedx.learning.veritical_block_child.render.started.v1 - - `2022-08-18 `_ - - * - `VerticalBlockRenderCompleted `_ - - org.openedx.learning.veritical_block.render.completed.v1 - - `2022-02-18 `_ diff --git a/docs/hooks/index.rst b/docs/hooks/index.rst deleted file mode 100644 index 99cb25133cd2..000000000000 --- a/docs/hooks/index.rst +++ /dev/null @@ -1,50 +0,0 @@ -Open edX Hooks Extension Framework -================================== - -To sustain the growth of the Open edX ecosystem, the business rules of the -platform must be open for extension following the open-closed principle. This -framework allows developers to do just that without needing to fork and modify -the main edx-platform repository. - - -Context -------- - -Hooks are predefined places in the edx-platform core where externally defined -functions can take place. In some cases, those functions can alter what the user -sees or experiences in the platform. Other cases are informative only. All cases -are meant to be extended using Open edX plugins and configuration. - -Hooks can be of two types, events and filters. Events are in essence signals, in -that they are sent in specific application places and whose listeners can extend -functionality. On the other hand Filters are passed data and can act on it -before this data is put back in the original application flow. In order to allow -extension developers to use the Events and Filters definitions on their plugins, -both kinds of hooks are defined in lightweight external libraries. - -* openedx-filters (`guide <./filters.rst>`_, `source code `_) -* openedx-events (`guide <./events.rst>`_, `source code `_) - -Hooks are designed with stability in mind. The main goal is that developers can -use them to change the functionality of the platform as needed and still be able -to migrate to newer open releases with very little to no development effort. In -the case of the events, this is detailed in the `versioning ADR`_ and the -`payload ADR`_. - -A longer description of the framework and it's history can be found in `OEP 50`_. - -.. _OEP 50: https://open-edx-proposals.readthedocs.io/en/latest/oep-0050-hooks-extension-framework.html -.. _versioning ADR: https://github.com/eduNEXT/openedx-events/blob/main/docs/decisions/0002-events-naming-and-versioning.rst -.. _payload ADR: https://github.com/eduNEXT/openedx-events/blob/main/docs/decisions/0003-events-payload.rst - -On the technical side events are implemented through django signals which makes -them run in the same python process as the lms or cms. Furthermore, events block -the running process. Listeners of an event are encouraged to monitor the -performance or use alternative arch patterns such as receiving the event and -defer to launching async tasks than do the slow processing. - -On the other hand, filters are implemented using a pipeline mechanism, that executes -a list of functions called ``steps`` configured through Django settings. Each -pipeline step receives a dictionary with data, process it and returns an output. During -this process, they can alter the application execution flow by halting the process -or modifying their input arguments. diff --git a/docs/index.rst b/docs/index.rst index 8d89969398bc..190ba12db906 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -20,6 +20,7 @@ locations. .. _Developer Documentation Index: https://openedx.atlassian.net/wiki/spaces/DOC/overview .. _Open edX Development space: https://openedx.atlassian.net/wiki/spaces/COMM/overview .. _Open edX ReadTheDocs: http://docs.edx.org/ +.. _Hooks Extensions Framework: https://docs.openedx.org/en/latest/developers/concepts/hooks_extension_framework.html .. toctree:: :maxdepth: 1 @@ -32,7 +33,6 @@ locations. how-tos/index references/index concepts/index - hooks/index extensions/tinymce_plugins .. grid:: 1 2 2 2 @@ -80,14 +80,16 @@ locations. :class-card: sd-shadow-md sd-p-2 :class-footer: sd-border-0 - * :doc:`hooks/index` + * `Hooks Extensions Framework`_ * :doc:`extensions/tinymce_plugins` +++ - .. button-ref:: hooks/index + .. button-link:: https://docs.openedx.org/en/latest/developers/concepts/hooks_extension_framework.html :color: primary :outline: :expand: + Hooks Extensions Framework + Change History **************