From c0161c8576e876b004bb90b768a7c2e5fc6be6e6 Mon Sep 17 00:00:00 2001 From: FUN MOOC Bot Date: Fri, 21 Apr 2023 09:38:50 +0000 Subject: [PATCH] Deploy website - based on c126d1940a3bef1d52515dcbe088d03e05d2b4aa --- 404.html | 10 ++-- assets/js/061b9ec0.9c3a0854.js | 1 - assets/js/061b9ec0.bfb49803.js | 1 + assets/js/0add6094.d6475b98.js | 1 - assets/js/0add6094.ee28a2c0.js | 1 + assets/js/0b704f48.a57f152d.js | 1 - assets/js/0b704f48.a65ce7f2.js | 1 + assets/js/0d621e61.706236e8.js | 1 + assets/js/0d621e61.cd820561.js | 1 - assets/js/1681c897.04898a5f.js | 1 - assets/js/1681c897.7941d6c3.js | 1 + assets/js/18b93cb3.436283f7.js | 1 + assets/js/18b93cb3.fd17dff1.js | 1 - assets/js/26e576cc.6a8ab89b.js | 1 - assets/js/26e576cc.f7bdcbb6.js | 1 + assets/js/278477af.3c3b9039.js | 1 + assets/js/2bd76b34.8eea0d83.js | 1 - assets/js/2bd76b34.aa933374.js | 1 + assets/js/2fbb3042.6321f6cb.js | 1 - assets/js/2fbb3042.b08d134d.js | 1 + assets/js/34ff41bb.c7ff2118.js | 1 + ...39249.cbc8ccc8.js => 378f3d86.4c7d379d.js} | 2 +- assets/js/384badf6.dac3f2fb.js | 1 + assets/js/39917643.9709dbf5.js | 1 + assets/js/3a6f03ad.7b6e5a8b.js | 1 + assets/js/3c7d7aed.32b7a062.js | 1 + assets/js/3c7d7aed.9a20e8f0.js | 1 - assets/js/3e42226b.6fbd735c.js | 1 + assets/js/4220d24c.30598178.js | 1 + assets/js/4220d24c.e7b32db4.js | 1 - assets/js/43dd260b.e5f7cca6.js | 1 + assets/js/4b3d2ce7.4f37e633.js | 1 - assets/js/4b3d2ce7.d67d80b5.js | 1 + assets/js/4ee76bca.8c6cf3ab.js | 1 - assets/js/4ee76bca.a4cfa547.js | 1 + assets/js/656a92e7.0acc6b84.js | 1 - assets/js/656a92e7.1edcbff1.js | 1 + assets/js/65c0386d.75cf76a7.js | 1 + assets/js/66cba0bd.7bc81ecf.js | 1 - assets/js/66cba0bd.b803fec5.js | 1 + assets/js/78ba9b90.6b00eca7.js | 1 + assets/js/78bf798d.801026b8.js | 1 + assets/js/7b4c5d89.9088e01f.js | 1 - assets/js/7b4c5d89.bc1c0682.js | 1 + assets/js/7c81245b.45d9eaad.js | 1 + assets/js/84eeefe6.3cec538d.js | 1 + assets/js/8865241c.1c472e13.js | 1 + assets/js/899fb7ad.10b5771d.js | 1 + assets/js/899fb7ad.a7de17f5.js | 1 - ...a9b90.b7a6271b.js => 94b95b8f.f4008e67.js} | 2 +- assets/js/95033604.6df40db6.js | 1 + assets/js/9afd861b.301076d6.js | 1 - assets/js/9afd861b.996e1e7f.js | 1 + ...1245b.df5fb444.js => a04054f5.06dcfff1.js} | 2 +- assets/js/a99a7a84.211b536d.js | 1 + assets/js/b064ae39.19e8d18c.js | 1 - assets/js/b064ae39.f8ec9cdd.js | 1 + assets/js/b68a0da6.34973966.js | 1 + assets/js/b68a0da6.e04b6cf4.js | 1 - assets/js/c3f10aa7.d4c8fabf.js | 1 + assets/js/d02faa29.f7d52191.js | 1 + assets/js/d1239249.19877c32.js | 1 + assets/js/d32dcc20.cd61070a.js | 1 + assets/js/d565a856.21e02296.js | 1 + assets/js/e3683d7e.b2f6617a.js | 1 + assets/js/ec9e4b3c.468e0049.js | 1 + assets/js/f2be4710.cca1fad0.js | 1 - assets/js/f2be4710.dc4825eb.js | 1 + assets/js/main.6c495275.js | 2 + ...CENSE.txt => main.6c495275.js.LICENSE.txt} | 0 assets/js/main.764693fa.js | 2 - assets/js/runtime~main.0869f37c.js | 1 + assets/js/runtime~main.ae4848ec.js | 1 - docs/1.12/contributing-guide/index.html | 10 ++-- docs/1.12/css-guidelines/index.html | 10 ++-- docs/1.12/discover/index.html | 10 ++-- docs/1.12/django-react-interop/index.html | 10 ++-- docs/1.12/docker-development/index.html | 10 ++-- docs/1.12/native-installation/index.html | 10 ++-- docs/1.13/building-the-frontend/index.html | 10 ++-- docs/1.13/contributing-guide/index.html | 10 ++-- docs/1.13/css-guidelines/index.html | 10 ++-- docs/1.13/discover/index.html | 10 ++-- docs/1.13/django-react-interop/index.html | 10 ++-- docs/1.13/docker-development/index.html | 10 ++-- docs/1.13/native-installation/index.html | 10 ++-- docs/1.14/building-the-frontend/index.html | 10 ++-- docs/1.14/contributing-guide/index.html | 10 ++-- docs/1.14/css-guidelines/index.html | 10 ++-- docs/1.14/discover/index.html | 10 ++-- docs/1.14/django-react-interop/index.html | 10 ++-- docs/1.14/docker-development/index.html | 10 ++-- docs/1.14/native-installation/index.html | 10 ++-- docs/1.15/building-the-frontend/index.html | 10 ++-- docs/1.15/contributing-guide/index.html | 10 ++-- docs/1.15/css-guidelines/index.html | 10 ++-- docs/1.15/discover/index.html | 10 ++-- docs/1.15/django-react-interop/index.html | 10 ++-- docs/1.15/docker-development/index.html | 10 ++-- docs/1.15/native-installation/index.html | 10 ++-- docs/1.16/accessibility-testing/index.html | 10 ++-- docs/1.16/building-the-frontend/index.html | 10 ++-- docs/1.16/contributing-guide/index.html | 10 ++-- docs/1.16/css-guidelines/index.html | 10 ++-- docs/1.16/discover/index.html | 10 ++-- docs/1.16/django-react-interop/index.html | 10 ++-- docs/1.16/docker-development/index.html | 10 ++-- docs/1.16/native-installation/index.html | 10 ++-- docs/1.17/accessibility-testing/index.html | 10 ++-- docs/1.17/building-the-frontend/index.html | 10 ++-- docs/1.17/contributing-guide/index.html | 10 ++-- docs/1.17/css-guidelines/index.html | 10 ++-- docs/1.17/discover/index.html | 10 ++-- docs/1.17/django-react-interop/index.html | 10 ++-- docs/1.17/docker-development/index.html | 10 ++-- docs/1.17/native-installation/index.html | 10 ++-- docs/2.0.0/accessibility-testing/index.html | 10 ++-- docs/2.0.0/building-the-frontend/index.html | 10 ++-- docs/2.0.0/contributing-guide/index.html | 10 ++-- docs/2.0.0/css-guidelines/index.html | 10 ++-- docs/2.0.0/discover/index.html | 10 ++-- docs/2.0.0/django-react-interop/index.html | 10 ++-- docs/2.0.0/docker-development/index.html | 10 ++-- docs/2.0.0/frontend-overrides/index.html | 10 ++-- docs/2.0.0/lms-connection/index.html | 10 ++-- docs/2.0.0/native-installation/index.html | 10 ++-- docs/2.0.1/accessibility-testing/index.html | 10 ++-- docs/2.0.1/building-the-frontend/index.html | 10 ++-- docs/2.0.1/contributing-guide/index.html | 10 ++-- docs/2.0.1/css-guidelines/index.html | 10 ++-- docs/2.0.1/discover/index.html | 10 ++-- docs/2.0.1/django-react-interop/index.html | 10 ++-- docs/2.0.1/docker-development/index.html | 10 ++-- docs/2.0.1/frontend-overrides/index.html | 10 ++-- docs/2.0.1/lms-connection/index.html | 10 ++-- docs/2.0.1/native-installation/index.html | 10 ++-- docs/2.1.0/accessibility-testing/index.html | 10 ++-- docs/2.1.0/building-the-frontend/index.html | 10 ++-- docs/2.1.0/contributing-guide/index.html | 10 ++-- docs/2.1.0/css-guidelines/index.html | 10 ++-- docs/2.1.0/discover/index.html | 10 ++-- docs/2.1.0/django-react-interop/index.html | 10 ++-- docs/2.1.0/docker-development/index.html | 10 ++-- docs/2.1.0/frontend-overrides/index.html | 10 ++-- docs/2.1.0/lms-connection/index.html | 10 ++-- docs/2.1.0/native-installation/index.html | 10 ++-- docs/2.10.0/accessibility-testing/index.html | 10 ++-- .../course-run-synchronization-api/index.html | 10 ++-- docs/2.10.0/building-the-frontend/index.html | 10 ++-- docs/2.10.0/contributing-guide/index.html | 10 ++-- docs/2.10.0/css-guidelines/index.html | 10 ++-- docs/2.10.0/discover/index.html | 10 ++-- .../displaying-connection-status/index.html | 10 ++-- docs/2.10.0/django-react-interop/index.html | 10 ++-- docs/2.10.0/docker-development/index.html | 10 ++-- docs/2.10.0/frontend-overrides/index.html | 10 ++-- docs/2.10.0/internationalization/index.html | 10 ++-- docs/2.10.0/lms-backends/index.html | 10 ++-- docs/2.10.0/lms-connection/index.html | 10 ++-- docs/2.10.0/native-installation/index.html | 10 ++-- .../synchronizing-course-runs/index.html | 10 ++-- docs/2.10.0/tls-connection/index.html | 10 ++-- docs/2.10.0/web-analytics/index.html | 10 ++-- docs/2.11.0/accessibility-testing/index.html | 10 ++-- .../course-run-synchronization-api/index.html | 10 ++-- docs/2.11.0/building-the-frontend/index.html | 10 ++-- docs/2.11.0/contributing-guide/index.html | 10 ++-- docs/2.11.0/css-guidelines/index.html | 10 ++-- docs/2.11.0/discover/index.html | 10 ++-- .../displaying-connection-status/index.html | 10 ++-- docs/2.11.0/django-react-interop/index.html | 10 ++-- docs/2.11.0/docker-development/index.html | 10 ++-- docs/2.11.0/frontend-overrides/index.html | 10 ++-- docs/2.11.0/internationalization/index.html | 10 ++-- docs/2.11.0/lms-backends/index.html | 10 ++-- docs/2.11.0/lms-connection/index.html | 10 ++-- docs/2.11.0/native-installation/index.html | 10 ++-- .../synchronizing-course-runs/index.html | 10 ++-- docs/2.11.0/tls-connection/index.html | 10 ++-- docs/2.11.0/web-analytics/index.html | 10 ++-- docs/2.12.0/accessibility-testing/index.html | 10 ++-- .../course-run-synchronization-api/index.html | 10 ++-- docs/2.12.0/building-the-frontend/index.html | 10 ++-- docs/2.12.0/contributing-guide/index.html | 10 ++-- docs/2.12.0/css-guidelines/index.html | 10 ++-- docs/2.12.0/discover/index.html | 10 ++-- .../displaying-connection-status/index.html | 10 ++-- docs/2.12.0/django-react-interop/index.html | 10 ++-- docs/2.12.0/docker-development/index.html | 10 ++-- docs/2.12.0/frontend-overrides/index.html | 10 ++-- docs/2.12.0/internationalization/index.html | 10 ++-- docs/2.12.0/lms-backends/index.html | 10 ++-- docs/2.12.0/lms-connection/index.html | 10 ++-- docs/2.12.0/native-installation/index.html | 10 ++-- .../synchronizing-course-runs/index.html | 10 ++-- docs/2.12.0/tls-connection/index.html | 10 ++-- docs/2.12.0/web-analytics/index.html | 10 ++-- docs/2.13.0/accessibility-testing/index.html | 10 ++-- .../course-run-synchronization-api/index.html | 10 ++-- docs/2.13.0/building-the-frontend/index.html | 10 ++-- docs/2.13.0/contributing-guide/index.html | 10 ++-- docs/2.13.0/css-guidelines/index.html | 10 ++-- docs/2.13.0/discover/index.html | 10 ++-- .../displaying-connection-status/index.html | 10 ++-- docs/2.13.0/django-react-interop/index.html | 10 ++-- docs/2.13.0/docker-development/index.html | 10 ++-- docs/2.13.0/frontend-overrides/index.html | 10 ++-- docs/2.13.0/internationalization/index.html | 10 ++-- docs/2.13.0/lms-backends/index.html | 10 ++-- docs/2.13.0/lms-connection/index.html | 10 ++-- docs/2.13.0/native-installation/index.html | 10 ++-- .../synchronizing-course-runs/index.html | 10 ++-- docs/2.13.0/tls-connection/index.html | 10 ++-- docs/2.13.0/web-analytics/index.html | 10 ++-- docs/2.14.0/accessibility-testing/index.html | 10 ++-- .../course-run-synchronization-api/index.html | 10 ++-- docs/2.14.0/building-the-frontend/index.html | 10 ++-- docs/2.14.0/contributing-guide/index.html | 10 ++-- docs/2.14.0/css-guidelines/index.html | 10 ++-- docs/2.14.0/discover/index.html | 10 ++-- .../displaying-connection-status/index.html | 10 ++-- docs/2.14.0/django-react-interop/index.html | 10 ++-- docs/2.14.0/docker-development/index.html | 10 ++-- docs/2.14.0/frontend-overrides/index.html | 10 ++-- docs/2.14.0/internationalization/index.html | 10 ++-- docs/2.14.0/lms-backends/index.html | 10 ++-- docs/2.14.0/lms-connection/index.html | 10 ++-- docs/2.14.0/native-installation/index.html | 10 ++-- .../synchronizing-course-runs/index.html | 10 ++-- docs/2.14.0/tls-connection/index.html | 10 ++-- docs/2.14.0/web-analytics/index.html | 10 ++-- docs/2.14.1/accessibility-testing/index.html | 10 ++-- .../course-run-synchronization-api/index.html | 10 ++-- docs/2.14.1/building-the-frontend/index.html | 10 ++-- docs/2.14.1/contributing-guide/index.html | 10 ++-- docs/2.14.1/cookiecutter/index.html | 10 ++-- docs/2.14.1/css-guidelines/index.html | 10 ++-- docs/2.14.1/discover/index.html | 10 ++-- .../displaying-connection-status/index.html | 10 ++-- docs/2.14.1/django-react-interop/index.html | 10 ++-- docs/2.14.1/docker-development/index.html | 10 ++-- docs/2.14.1/frontend-overrides/index.html | 10 ++-- docs/2.14.1/installation/index.html | 10 ++-- docs/2.14.1/internationalization/index.html | 10 ++-- docs/2.14.1/joanie-connection/index.html | 10 ++-- docs/2.14.1/lms-backends/index.html | 10 ++-- docs/2.14.1/lms-connection/index.html | 10 ++-- docs/2.14.1/native-installation/index.html | 10 ++-- .../synchronizing-course-runs/index.html | 10 ++-- docs/2.14.1/tls-connection/index.html | 10 ++-- docs/2.14.1/web-analytics/index.html | 10 ++-- docs/2.15.0/accessibility-testing/index.html | 10 ++-- .../course-run-synchronization-api/index.html | 10 ++-- docs/2.15.0/building-the-frontend/index.html | 10 ++-- docs/2.15.0/contributing-guide/index.html | 10 ++-- docs/2.15.0/cookiecutter/index.html | 10 ++-- docs/2.15.0/css-guidelines/index.html | 10 ++-- docs/2.15.0/discover/index.html | 10 ++-- .../displaying-connection-status/index.html | 10 ++-- docs/2.15.0/django-react-interop/index.html | 10 ++-- docs/2.15.0/docker-development/index.html | 10 ++-- docs/2.15.0/frontend-overrides/index.html | 10 ++-- docs/2.15.0/installation/index.html | 10 ++-- docs/2.15.0/internationalization/index.html | 10 ++-- docs/2.15.0/joanie-connection/index.html | 10 ++-- docs/2.15.0/lms-backends/index.html | 10 ++-- docs/2.15.0/lms-connection/index.html | 10 ++-- docs/2.15.0/native-installation/index.html | 10 ++-- .../synchronizing-course-runs/index.html | 10 ++-- docs/2.15.0/tls-connection/index.html | 10 ++-- docs/2.15.0/web-analytics/index.html | 10 ++-- docs/2.15.1/accessibility-testing/index.html | 10 ++-- .../course-run-synchronization-api/index.html | 10 ++-- docs/2.15.1/building-the-frontend/index.html | 10 ++-- docs/2.15.1/contributing-guide/index.html | 10 ++-- docs/2.15.1/cookiecutter/index.html | 10 ++-- docs/2.15.1/css-guidelines/index.html | 10 ++-- docs/2.15.1/discover/index.html | 10 ++-- .../displaying-connection-status/index.html | 10 ++-- docs/2.15.1/django-react-interop/index.html | 10 ++-- docs/2.15.1/docker-development/index.html | 10 ++-- docs/2.15.1/frontend-overrides/index.html | 10 ++-- docs/2.15.1/installation/index.html | 10 ++-- docs/2.15.1/internationalization/index.html | 10 ++-- docs/2.15.1/joanie-connection/index.html | 10 ++-- docs/2.15.1/lms-backends/index.html | 10 ++-- docs/2.15.1/lms-connection/index.html | 10 ++-- docs/2.15.1/native-installation/index.html | 10 ++-- .../synchronizing-course-runs/index.html | 10 ++-- docs/2.15.1/tls-connection/index.html | 10 ++-- docs/2.15.1/web-analytics/index.html | 10 ++-- docs/2.16.0/accessibility-testing/index.html | 10 ++-- .../course-run-synchronization-api/index.html | 10 ++-- docs/2.16.0/building-the-frontend/index.html | 10 ++-- docs/2.16.0/contributing-guide/index.html | 10 ++-- docs/2.16.0/cookiecutter/index.html | 10 ++-- docs/2.16.0/css-guidelines/index.html | 10 ++-- docs/2.16.0/discover/index.html | 10 ++-- .../displaying-connection-status/index.html | 10 ++-- docs/2.16.0/django-react-interop/index.html | 10 ++-- docs/2.16.0/docker-development/index.html | 10 ++-- docs/2.16.0/frontend-overrides/index.html | 10 ++-- docs/2.16.0/installation/index.html | 10 ++-- docs/2.16.0/internationalization/index.html | 10 ++-- docs/2.16.0/joanie-connection/index.html | 10 ++-- docs/2.16.0/lms-backends/index.html | 10 ++-- docs/2.16.0/lms-connection/index.html | 10 ++-- docs/2.16.0/native-installation/index.html | 10 ++-- .../synchronizing-course-runs/index.html | 10 ++-- docs/2.16.0/tls-connection/index.html | 10 ++-- docs/2.16.0/web-analytics/index.html | 10 ++-- docs/2.17.0/accessibility-testing/index.html | 10 ++-- .../course-run-synchronization-api/index.html | 10 ++-- docs/2.17.0/building-the-frontend/index.html | 10 ++-- docs/2.17.0/contributing-guide/index.html | 10 ++-- docs/2.17.0/cookiecutter/index.html | 10 ++-- docs/2.17.0/css-guidelines/index.html | 10 ++-- docs/2.17.0/discover/index.html | 10 ++-- .../displaying-connection-status/index.html | 10 ++-- docs/2.17.0/django-react-interop/index.html | 10 ++-- docs/2.17.0/docker-development/index.html | 10 ++-- docs/2.17.0/filters-customization/index.html | 10 ++-- docs/2.17.0/frontend-overrides/index.html | 10 ++-- docs/2.17.0/installation/index.html | 10 ++-- docs/2.17.0/internationalization/index.html | 10 ++-- docs/2.17.0/joanie-connection/index.html | 10 ++-- docs/2.17.0/lms-backends/index.html | 10 ++-- docs/2.17.0/lms-connection/index.html | 10 ++-- docs/2.17.0/native-installation/index.html | 10 ++-- .../synchronizing-course-runs/index.html | 10 ++-- docs/2.17.0/tls-connection/index.html | 10 ++-- docs/2.17.0/web-analytics/index.html | 10 ++-- docs/2.18.0/accessibility-testing/index.html | 10 ++-- .../course-run-synchronization-api/index.html | 10 ++-- docs/2.18.0/building-the-frontend/index.html | 10 ++-- docs/2.18.0/contributing-guide/index.html | 10 ++-- docs/2.18.0/cookiecutter/index.html | 10 ++-- docs/2.18.0/css-guidelines/index.html | 10 ++-- docs/2.18.0/discover/index.html | 10 ++-- .../displaying-connection-status/index.html | 10 ++-- docs/2.18.0/django-react-interop/index.html | 10 ++-- docs/2.18.0/docker-development/index.html | 10 ++-- docs/2.18.0/filters-customization/index.html | 10 ++-- docs/2.18.0/frontend-overrides/index.html | 10 ++-- docs/2.18.0/installation/index.html | 10 ++-- docs/2.18.0/internationalization/index.html | 10 ++-- docs/2.18.0/joanie-connection/index.html | 10 ++-- docs/2.18.0/lms-backends/index.html | 10 ++-- docs/2.18.0/lms-connection/index.html | 10 ++-- docs/2.18.0/native-installation/index.html | 10 ++-- .../synchronizing-course-runs/index.html | 10 ++-- docs/2.18.0/tls-connection/index.html | 10 ++-- docs/2.18.0/web-analytics/index.html | 10 ++-- docs/2.19.0/accessibility-testing/index.html | 10 ++-- .../course-run-synchronization-api/index.html | 10 ++-- docs/2.19.0/building-the-frontend/index.html | 10 ++-- docs/2.19.0/contributing-guide/index.html | 10 ++-- docs/2.19.0/cookiecutter/index.html | 10 ++-- docs/2.19.0/css-guidelines/index.html | 10 ++-- docs/2.19.0/discover/index.html | 10 ++-- .../displaying-connection-status/index.html | 10 ++-- docs/2.19.0/django-react-interop/index.html | 10 ++-- docs/2.19.0/docker-development/index.html | 10 ++-- docs/2.19.0/filters-customization/index.html | 10 ++-- docs/2.19.0/frontend-overrides/index.html | 10 ++-- docs/2.19.0/installation/index.html | 10 ++-- docs/2.19.0/internationalization/index.html | 10 ++-- docs/2.19.0/joanie-connection/index.html | 10 ++-- docs/2.19.0/lms-backends/index.html | 10 ++-- docs/2.19.0/lms-connection/index.html | 10 ++-- docs/2.19.0/native-installation/index.html | 10 ++-- .../synchronizing-course-runs/index.html | 10 ++-- docs/2.19.0/tls-connection/index.html | 10 ++-- docs/2.19.0/web-analytics/index.html | 10 ++-- docs/2.2.0/accessibility-testing/index.html | 10 ++-- docs/2.2.0/building-the-frontend/index.html | 10 ++-- docs/2.2.0/contributing-guide/index.html | 10 ++-- docs/2.2.0/css-guidelines/index.html | 10 ++-- docs/2.2.0/discover/index.html | 10 ++-- docs/2.2.0/django-react-interop/index.html | 10 ++-- docs/2.2.0/docker-development/index.html | 10 ++-- docs/2.2.0/frontend-overrides/index.html | 10 ++-- docs/2.2.0/internationalization/index.html | 10 ++-- docs/2.2.0/lms-connection/index.html | 10 ++-- docs/2.2.0/native-installation/index.html | 10 ++-- docs/2.20.0/accessibility-testing/index.html | 10 ++-- .../course-run-synchronization-api/index.html | 10 ++-- docs/2.20.0/building-the-frontend/index.html | 10 ++-- docs/2.20.0/contributing-guide/index.html | 10 ++-- docs/2.20.0/cookiecutter/index.html | 10 ++-- docs/2.20.0/css-guidelines/index.html | 10 ++-- docs/2.20.0/discover/index.html | 10 ++-- .../displaying-connection-status/index.html | 10 ++-- docs/2.20.0/django-react-interop/index.html | 10 ++-- docs/2.20.0/docker-development/index.html | 10 ++-- docs/2.20.0/filters-customization/index.html | 10 ++-- docs/2.20.0/frontend-overrides/index.html | 10 ++-- docs/2.20.0/installation/index.html | 10 ++-- docs/2.20.0/internationalization/index.html | 10 ++-- docs/2.20.0/joanie-connection/index.html | 10 ++-- docs/2.20.0/lms-backends/index.html | 10 ++-- docs/2.20.0/lms-connection/index.html | 10 ++-- docs/2.20.0/native-installation/index.html | 10 ++-- .../synchronizing-course-runs/index.html | 10 ++-- docs/2.20.0/tls-connection/index.html | 10 ++-- docs/2.20.0/web-analytics/index.html | 10 ++-- docs/2.20.1/accessibility-testing/index.html | 10 ++-- .../course-run-synchronization-api/index.html | 10 ++-- docs/2.20.1/building-the-frontend/index.html | 10 ++-- docs/2.20.1/contributing-guide/index.html | 10 ++-- docs/2.20.1/cookiecutter/index.html | 10 ++-- docs/2.20.1/css-guidelines/index.html | 10 ++-- docs/2.20.1/discover/index.html | 10 ++-- .../displaying-connection-status/index.html | 10 ++-- docs/2.20.1/django-react-interop/index.html | 10 ++-- docs/2.20.1/docker-development/index.html | 10 ++-- docs/2.20.1/filters-customization/index.html | 10 ++-- docs/2.20.1/frontend-overrides/index.html | 10 ++-- docs/2.20.1/installation/index.html | 10 ++-- docs/2.20.1/internationalization/index.html | 10 ++-- docs/2.20.1/joanie-connection/index.html | 10 ++-- docs/2.20.1/lms-backends/index.html | 10 ++-- docs/2.20.1/lms-connection/index.html | 10 ++-- docs/2.20.1/native-installation/index.html | 10 ++-- .../synchronizing-course-runs/index.html | 10 ++-- docs/2.20.1/tls-connection/index.html | 10 ++-- docs/2.20.1/web-analytics/index.html | 10 ++-- docs/2.21.0/accessibility-testing/index.html | 10 ++-- .../course-run-synchronization-api/index.html | 10 ++-- docs/2.21.0/building-the-frontend/index.html | 10 ++-- docs/2.21.0/contributing-guide/index.html | 10 ++-- docs/2.21.0/cookiecutter/index.html | 10 ++-- docs/2.21.0/css-guidelines/index.html | 10 ++-- docs/2.21.0/discover/index.html | 10 ++-- .../displaying-connection-status/index.html | 10 ++-- docs/2.21.0/django-react-interop/index.html | 10 ++-- docs/2.21.0/docker-development/index.html | 10 ++-- docs/2.21.0/filters-customization/index.html | 10 ++-- docs/2.21.0/frontend-overrides/index.html | 10 ++-- docs/2.21.0/installation/index.html | 10 ++-- docs/2.21.0/internationalization/index.html | 10 ++-- docs/2.21.0/joanie-connection/index.html | 10 ++-- docs/2.21.0/lms-backends/index.html | 10 ++-- docs/2.21.0/lms-connection/index.html | 10 ++-- docs/2.21.0/native-installation/index.html | 10 ++-- .../synchronizing-course-runs/index.html | 10 ++-- docs/2.21.0/tls-connection/index.html | 10 ++-- docs/2.21.0/web-analytics/index.html | 10 ++-- docs/2.21.1/accessibility-testing/index.html | 16 ++++++ .../course-run-synchronization-api/index.html | 25 +++++++++ docs/2.21.1/building-the-frontend/index.html | 16 ++++++ docs/2.21.1/contributing-guide/index.html | 22 ++++++++ docs/2.21.1/cookiecutter/index.html | 36 +++++++++++++ docs/2.21.1/css-guidelines/index.html | 16 ++++++ docs/2.21.1/discover/index.html | 27 ++++++++++ .../displaying-connection-status/index.html | 32 +++++++++++ docs/2.21.1/django-react-interop/index.html | 16 ++++++ docs/2.21.1/docker-development/index.html | 35 ++++++++++++ docs/2.21.1/filters-customization/index.html | 54 +++++++++++++++++++ docs/2.21.1/frontend-overrides/index.html | 16 ++++++ docs/2.21.1/installation/index.html | 41 ++++++++++++++ docs/2.21.1/internationalization/index.html | 28 ++++++++++ docs/2.21.1/joanie-connection/index.html | 46 ++++++++++++++++ docs/2.21.1/lms-backends/index.html | 42 +++++++++++++++ docs/2.21.1/lms-connection/index.html | 37 +++++++++++++ docs/2.21.1/native-installation/index.html | 40 ++++++++++++++ .../synchronizing-course-runs/index.html | 35 ++++++++++++ docs/2.21.1/tls-connection/index.html | 38 +++++++++++++ docs/2.21.1/web-analytics/index.html | 22 ++++++++ docs/2.3.0/accessibility-testing/index.html | 10 ++-- docs/2.3.0/building-the-frontend/index.html | 10 ++-- docs/2.3.0/contributing-guide/index.html | 10 ++-- docs/2.3.0/css-guidelines/index.html | 10 ++-- docs/2.3.0/discover/index.html | 10 ++-- docs/2.3.0/django-react-interop/index.html | 10 ++-- docs/2.3.0/docker-development/index.html | 10 ++-- docs/2.3.0/frontend-overrides/index.html | 10 ++-- docs/2.3.0/internationalization/index.html | 10 ++-- docs/2.3.0/lms-connection/index.html | 10 ++-- docs/2.3.0/native-installation/index.html | 10 ++-- docs/2.3.1/accessibility-testing/index.html | 10 ++-- docs/2.3.1/building-the-frontend/index.html | 10 ++-- docs/2.3.1/contributing-guide/index.html | 10 ++-- docs/2.3.1/css-guidelines/index.html | 10 ++-- docs/2.3.1/discover/index.html | 10 ++-- docs/2.3.1/django-react-interop/index.html | 10 ++-- docs/2.3.1/docker-development/index.html | 10 ++-- docs/2.3.1/frontend-overrides/index.html | 10 ++-- docs/2.3.1/internationalization/index.html | 10 ++-- docs/2.3.1/lms-connection/index.html | 10 ++-- docs/2.3.1/native-installation/index.html | 10 ++-- docs/2.3.2/accessibility-testing/index.html | 10 ++-- docs/2.3.2/building-the-frontend/index.html | 10 ++-- docs/2.3.2/contributing-guide/index.html | 10 ++-- docs/2.3.2/css-guidelines/index.html | 10 ++-- docs/2.3.2/discover/index.html | 10 ++-- docs/2.3.2/django-react-interop/index.html | 10 ++-- docs/2.3.2/docker-development/index.html | 10 ++-- docs/2.3.2/frontend-overrides/index.html | 10 ++-- docs/2.3.2/internationalization/index.html | 10 ++-- docs/2.3.2/lms-connection/index.html | 10 ++-- docs/2.3.2/native-installation/index.html | 10 ++-- docs/2.3.3/accessibility-testing/index.html | 10 ++-- docs/2.3.3/building-the-frontend/index.html | 10 ++-- docs/2.3.3/contributing-guide/index.html | 10 ++-- docs/2.3.3/css-guidelines/index.html | 10 ++-- docs/2.3.3/discover/index.html | 10 ++-- docs/2.3.3/django-react-interop/index.html | 10 ++-- docs/2.3.3/docker-development/index.html | 10 ++-- docs/2.3.3/frontend-overrides/index.html | 10 ++-- docs/2.3.3/internationalization/index.html | 10 ++-- docs/2.3.3/lms-connection/index.html | 10 ++-- docs/2.3.3/native-installation/index.html | 10 ++-- docs/2.4.0/accessibility-testing/index.html | 10 ++-- docs/2.4.0/building-the-frontend/index.html | 10 ++-- docs/2.4.0/contributing-guide/index.html | 10 ++-- docs/2.4.0/css-guidelines/index.html | 10 ++-- docs/2.4.0/discover/index.html | 10 ++-- docs/2.4.0/django-react-interop/index.html | 10 ++-- docs/2.4.0/docker-development/index.html | 10 ++-- docs/2.4.0/frontend-overrides/index.html | 10 ++-- docs/2.4.0/internationalization/index.html | 10 ++-- docs/2.4.0/lms-connection/index.html | 10 ++-- docs/2.4.0/native-installation/index.html | 10 ++-- docs/2.5.0/accessibility-testing/index.html | 10 ++-- docs/2.5.0/building-the-frontend/index.html | 10 ++-- docs/2.5.0/contributing-guide/index.html | 10 ++-- docs/2.5.0/css-guidelines/index.html | 10 ++-- docs/2.5.0/discover/index.html | 10 ++-- docs/2.5.0/django-react-interop/index.html | 10 ++-- docs/2.5.0/docker-development/index.html | 10 ++-- docs/2.5.0/frontend-overrides/index.html | 10 ++-- docs/2.5.0/internationalization/index.html | 10 ++-- docs/2.5.0/lms-connection/index.html | 10 ++-- docs/2.5.0/native-installation/index.html | 10 ++-- docs/2.6.0/accessibility-testing/index.html | 10 ++-- docs/2.6.0/building-the-frontend/index.html | 10 ++-- docs/2.6.0/contributing-guide/index.html | 10 ++-- docs/2.6.0/css-guidelines/index.html | 10 ++-- docs/2.6.0/discover/index.html | 10 ++-- docs/2.6.0/django-react-interop/index.html | 10 ++-- docs/2.6.0/docker-development/index.html | 10 ++-- docs/2.6.0/frontend-overrides/index.html | 10 ++-- docs/2.6.0/internationalization/index.html | 10 ++-- docs/2.6.0/lms-connection/index.html | 10 ++-- docs/2.6.0/native-installation/index.html | 10 ++-- docs/2.7.0/accessibility-testing/index.html | 10 ++-- docs/2.7.0/building-the-frontend/index.html | 10 ++-- docs/2.7.0/contributing-guide/index.html | 10 ++-- docs/2.7.0/css-guidelines/index.html | 10 ++-- docs/2.7.0/discover/index.html | 10 ++-- docs/2.7.0/django-react-interop/index.html | 10 ++-- docs/2.7.0/docker-development/index.html | 10 ++-- docs/2.7.0/frontend-overrides/index.html | 10 ++-- docs/2.7.0/internationalization/index.html | 10 ++-- docs/2.7.0/lms-connection/index.html | 10 ++-- docs/2.7.0/native-installation/index.html | 10 ++-- docs/2.7.1/accessibility-testing/index.html | 10 ++-- docs/2.7.1/building-the-frontend/index.html | 10 ++-- docs/2.7.1/contributing-guide/index.html | 10 ++-- docs/2.7.1/css-guidelines/index.html | 10 ++-- docs/2.7.1/discover/index.html | 10 ++-- docs/2.7.1/django-react-interop/index.html | 10 ++-- docs/2.7.1/docker-development/index.html | 10 ++-- docs/2.7.1/frontend-overrides/index.html | 10 ++-- docs/2.7.1/internationalization/index.html | 10 ++-- docs/2.7.1/lms-connection/index.html | 10 ++-- docs/2.7.1/native-installation/index.html | 10 ++-- docs/2.8.0/accessibility-testing/index.html | 10 ++-- .../course-run-synchronization-api/index.html | 10 ++-- docs/2.8.0/building-the-frontend/index.html | 10 ++-- docs/2.8.0/contributing-guide/index.html | 10 ++-- docs/2.8.0/css-guidelines/index.html | 10 ++-- docs/2.8.0/discover/index.html | 10 ++-- .../displaying-connection-status/index.html | 10 ++-- docs/2.8.0/django-react-interop/index.html | 10 ++-- docs/2.8.0/docker-development/index.html | 10 ++-- docs/2.8.0/frontend-overrides/index.html | 10 ++-- docs/2.8.0/internationalization/index.html | 10 ++-- docs/2.8.0/lms-backends/index.html | 10 ++-- docs/2.8.0/lms-connection/index.html | 10 ++-- docs/2.8.0/native-installation/index.html | 10 ++-- .../synchronizing-course-runs/index.html | 10 ++-- docs/2.8.0/tls-connection/index.html | 10 ++-- docs/2.8.0/web-analytics/index.html | 10 ++-- docs/2.8.1/accessibility-testing/index.html | 10 ++-- .../course-run-synchronization-api/index.html | 10 ++-- docs/2.8.1/building-the-frontend/index.html | 10 ++-- docs/2.8.1/contributing-guide/index.html | 10 ++-- docs/2.8.1/css-guidelines/index.html | 10 ++-- docs/2.8.1/discover/index.html | 10 ++-- .../displaying-connection-status/index.html | 10 ++-- docs/2.8.1/django-react-interop/index.html | 10 ++-- docs/2.8.1/docker-development/index.html | 10 ++-- docs/2.8.1/frontend-overrides/index.html | 10 ++-- docs/2.8.1/internationalization/index.html | 10 ++-- docs/2.8.1/lms-backends/index.html | 10 ++-- docs/2.8.1/lms-connection/index.html | 10 ++-- docs/2.8.1/native-installation/index.html | 10 ++-- .../synchronizing-course-runs/index.html | 10 ++-- docs/2.8.1/tls-connection/index.html | 10 ++-- docs/2.8.1/web-analytics/index.html | 10 ++-- docs/2.8.2/accessibility-testing/index.html | 10 ++-- .../course-run-synchronization-api/index.html | 10 ++-- docs/2.8.2/building-the-frontend/index.html | 10 ++-- docs/2.8.2/contributing-guide/index.html | 10 ++-- docs/2.8.2/css-guidelines/index.html | 10 ++-- docs/2.8.2/discover/index.html | 10 ++-- .../displaying-connection-status/index.html | 10 ++-- docs/2.8.2/django-react-interop/index.html | 10 ++-- docs/2.8.2/docker-development/index.html | 10 ++-- docs/2.8.2/frontend-overrides/index.html | 10 ++-- docs/2.8.2/internationalization/index.html | 10 ++-- docs/2.8.2/lms-backends/index.html | 10 ++-- docs/2.8.2/lms-connection/index.html | 10 ++-- docs/2.8.2/native-installation/index.html | 10 ++-- .../synchronizing-course-runs/index.html | 10 ++-- docs/2.8.2/tls-connection/index.html | 10 ++-- docs/2.8.2/web-analytics/index.html | 10 ++-- docs/2.9.0/accessibility-testing/index.html | 10 ++-- .../course-run-synchronization-api/index.html | 10 ++-- docs/2.9.0/building-the-frontend/index.html | 10 ++-- docs/2.9.0/contributing-guide/index.html | 10 ++-- docs/2.9.0/css-guidelines/index.html | 10 ++-- docs/2.9.0/discover/index.html | 10 ++-- .../displaying-connection-status/index.html | 10 ++-- docs/2.9.0/django-react-interop/index.html | 10 ++-- docs/2.9.0/docker-development/index.html | 10 ++-- docs/2.9.0/frontend-overrides/index.html | 10 ++-- docs/2.9.0/internationalization/index.html | 10 ++-- docs/2.9.0/lms-backends/index.html | 10 ++-- docs/2.9.0/lms-connection/index.html | 10 ++-- docs/2.9.0/native-installation/index.html | 10 ++-- .../synchronizing-course-runs/index.html | 10 ++-- docs/2.9.0/tls-connection/index.html | 10 ++-- docs/2.9.0/web-analytics/index.html | 10 ++-- docs/2.9.1/accessibility-testing/index.html | 10 ++-- .../course-run-synchronization-api/index.html | 10 ++-- docs/2.9.1/building-the-frontend/index.html | 10 ++-- docs/2.9.1/contributing-guide/index.html | 10 ++-- docs/2.9.1/css-guidelines/index.html | 10 ++-- docs/2.9.1/discover/index.html | 10 ++-- .../displaying-connection-status/index.html | 10 ++-- docs/2.9.1/django-react-interop/index.html | 10 ++-- docs/2.9.1/docker-development/index.html | 10 ++-- docs/2.9.1/frontend-overrides/index.html | 10 ++-- docs/2.9.1/internationalization/index.html | 10 ++-- docs/2.9.1/lms-backends/index.html | 10 ++-- docs/2.9.1/lms-connection/index.html | 10 ++-- docs/2.9.1/native-installation/index.html | 10 ++-- .../synchronizing-course-runs/index.html | 10 ++-- docs/2.9.1/tls-connection/index.html | 10 ++-- docs/2.9.1/web-analytics/index.html | 10 ++-- docs/accessibility-testing/index.html | 14 ++--- .../course-run-synchronization-api/index.html | 16 +++--- docs/building-the-frontend/index.html | 14 ++--- docs/contributing-guide/index.html | 16 +++--- docs/cookiecutter/index.html | 20 +++---- docs/css-guidelines/index.html | 14 ++--- docs/discover/index.html | 16 +++--- docs/displaying-connection-status/index.html | 16 +++--- docs/django-react-interop/index.html | 14 ++--- docs/docker-development/index.html | 16 +++--- docs/filters-customization/index.html | 16 +++--- docs/frontend-overrides/index.html | 14 ++--- docs/installation/index.html | 16 +++--- docs/internationalization/index.html | 16 +++--- docs/joanie-connection/index.html | 16 +++--- docs/lms-backends/index.html | 16 +++--- docs/lms-connection/index.html | 16 +++--- docs/native-installation/index.html | 16 +++--- docs/next/accessibility-testing/index.html | 10 ++-- .../course-run-synchronization-api/index.html | 10 ++-- docs/next/building-the-frontend/index.html | 10 ++-- docs/next/contributing-guide/index.html | 10 ++-- docs/next/cookiecutter/index.html | 16 +++--- docs/next/css-guidelines/index.html | 10 ++-- docs/next/discover/index.html | 10 ++-- .../displaying-connection-status/index.html | 10 ++-- docs/next/django-react-interop/index.html | 10 ++-- docs/next/docker-development/index.html | 10 ++-- docs/next/filters-customization/index.html | 10 ++-- docs/next/frontend-overrides/index.html | 10 ++-- docs/next/installation/index.html | 10 ++-- docs/next/internationalization/index.html | 10 ++-- docs/next/joanie-connection/index.html | 10 ++-- docs/next/lms-backends/index.html | 10 ++-- docs/next/lms-connection/index.html | 10 ++-- docs/next/native-installation/index.html | 10 ++-- .../next/synchronizing-course-runs/index.html | 10 ++-- docs/next/tls-connection/index.html | 10 ++-- docs/next/web-analytics/index.html | 10 ++-- docs/synchronizing-course-runs/index.html | 16 +++--- docs/tls-connection/index.html | 16 +++--- docs/web-analytics/index.html | 16 +++--- help/index.html | 10 ++-- index.html | 10 ++-- sitemap.xml | 2 +- users/index.html | 10 ++-- versions/index.html | 10 ++-- 700 files changed, 3783 insertions(+), 3121 deletions(-) delete mode 100644 assets/js/061b9ec0.9c3a0854.js create mode 100644 assets/js/061b9ec0.bfb49803.js delete mode 100644 assets/js/0add6094.d6475b98.js create mode 100644 assets/js/0add6094.ee28a2c0.js delete mode 100644 assets/js/0b704f48.a57f152d.js create mode 100644 assets/js/0b704f48.a65ce7f2.js create mode 100644 assets/js/0d621e61.706236e8.js delete mode 100644 assets/js/0d621e61.cd820561.js delete mode 100644 assets/js/1681c897.04898a5f.js create mode 100644 assets/js/1681c897.7941d6c3.js create mode 100644 assets/js/18b93cb3.436283f7.js delete mode 100644 assets/js/18b93cb3.fd17dff1.js delete mode 100644 assets/js/26e576cc.6a8ab89b.js create mode 100644 assets/js/26e576cc.f7bdcbb6.js create mode 100644 assets/js/278477af.3c3b9039.js delete mode 100644 assets/js/2bd76b34.8eea0d83.js create mode 100644 assets/js/2bd76b34.aa933374.js delete mode 100644 assets/js/2fbb3042.6321f6cb.js create mode 100644 assets/js/2fbb3042.b08d134d.js create mode 100644 assets/js/34ff41bb.c7ff2118.js rename assets/js/{d1239249.cbc8ccc8.js => 378f3d86.4c7d379d.js} (95%) create mode 100644 assets/js/384badf6.dac3f2fb.js create mode 100644 assets/js/39917643.9709dbf5.js create mode 100644 assets/js/3a6f03ad.7b6e5a8b.js create mode 100644 assets/js/3c7d7aed.32b7a062.js delete mode 100644 assets/js/3c7d7aed.9a20e8f0.js create mode 100644 assets/js/3e42226b.6fbd735c.js create mode 100644 assets/js/4220d24c.30598178.js delete mode 100644 assets/js/4220d24c.e7b32db4.js create mode 100644 assets/js/43dd260b.e5f7cca6.js delete mode 100644 assets/js/4b3d2ce7.4f37e633.js create mode 100644 assets/js/4b3d2ce7.d67d80b5.js delete mode 100644 assets/js/4ee76bca.8c6cf3ab.js create mode 100644 assets/js/4ee76bca.a4cfa547.js delete mode 100644 assets/js/656a92e7.0acc6b84.js create mode 100644 assets/js/656a92e7.1edcbff1.js create mode 100644 assets/js/65c0386d.75cf76a7.js delete mode 100644 assets/js/66cba0bd.7bc81ecf.js create mode 100644 assets/js/66cba0bd.b803fec5.js create mode 100644 assets/js/78ba9b90.6b00eca7.js create mode 100644 assets/js/78bf798d.801026b8.js delete mode 100644 assets/js/7b4c5d89.9088e01f.js create mode 100644 assets/js/7b4c5d89.bc1c0682.js create mode 100644 assets/js/7c81245b.45d9eaad.js create mode 100644 assets/js/84eeefe6.3cec538d.js create mode 100644 assets/js/8865241c.1c472e13.js create mode 100644 assets/js/899fb7ad.10b5771d.js delete mode 100644 assets/js/899fb7ad.a7de17f5.js rename assets/js/{78ba9b90.b7a6271b.js => 94b95b8f.f4008e67.js} (72%) create mode 100644 assets/js/95033604.6df40db6.js delete mode 100644 assets/js/9afd861b.301076d6.js create mode 100644 assets/js/9afd861b.996e1e7f.js rename assets/js/{7c81245b.df5fb444.js => a04054f5.06dcfff1.js} (91%) create mode 100644 assets/js/a99a7a84.211b536d.js delete mode 100644 assets/js/b064ae39.19e8d18c.js create mode 100644 assets/js/b064ae39.f8ec9cdd.js create mode 100644 assets/js/b68a0da6.34973966.js delete mode 100644 assets/js/b68a0da6.e04b6cf4.js create mode 100644 assets/js/c3f10aa7.d4c8fabf.js create mode 100644 assets/js/d02faa29.f7d52191.js create mode 100644 assets/js/d1239249.19877c32.js create mode 100644 assets/js/d32dcc20.cd61070a.js create mode 100644 assets/js/d565a856.21e02296.js create mode 100644 assets/js/e3683d7e.b2f6617a.js create mode 100644 assets/js/ec9e4b3c.468e0049.js delete mode 100644 assets/js/f2be4710.cca1fad0.js create mode 100644 assets/js/f2be4710.dc4825eb.js create mode 100644 assets/js/main.6c495275.js rename assets/js/{main.764693fa.js.LICENSE.txt => main.6c495275.js.LICENSE.txt} (100%) delete mode 100644 assets/js/main.764693fa.js create mode 100644 assets/js/runtime~main.0869f37c.js delete mode 100644 assets/js/runtime~main.ae4848ec.js create mode 100644 docs/2.21.1/accessibility-testing/index.html create mode 100644 docs/2.21.1/api/course-run-synchronization-api/index.html create mode 100644 docs/2.21.1/building-the-frontend/index.html create mode 100644 docs/2.21.1/contributing-guide/index.html create mode 100644 docs/2.21.1/cookiecutter/index.html create mode 100644 docs/2.21.1/css-guidelines/index.html create mode 100644 docs/2.21.1/discover/index.html create mode 100644 docs/2.21.1/displaying-connection-status/index.html create mode 100644 docs/2.21.1/django-react-interop/index.html create mode 100644 docs/2.21.1/docker-development/index.html create mode 100644 docs/2.21.1/filters-customization/index.html create mode 100644 docs/2.21.1/frontend-overrides/index.html create mode 100644 docs/2.21.1/installation/index.html create mode 100644 docs/2.21.1/internationalization/index.html create mode 100644 docs/2.21.1/joanie-connection/index.html create mode 100644 docs/2.21.1/lms-backends/index.html create mode 100644 docs/2.21.1/lms-connection/index.html create mode 100644 docs/2.21.1/native-installation/index.html create mode 100644 docs/2.21.1/synchronizing-course-runs/index.html create mode 100644 docs/2.21.1/tls-connection/index.html create mode 100644 docs/2.21.1/web-analytics/index.html diff --git a/404.html b/404.html index 5cf36e2d14..a192457184 100644 --- a/404.html +++ b/404.html @@ -4,13 +4,13 @@ Page Not Found | Richie - - + +
-
Skip to main content

Page Not Found

We could not find what you were looking for.

Please contact the owner of the site that linked you to the original URL and let them know their link is broken.

- - +
Skip to main content

Page Not Found

We could not find what you were looking for.

Please contact the owner of the site that linked you to the original URL and let them know their link is broken.

+ + \ No newline at end of file diff --git a/assets/js/061b9ec0.9c3a0854.js b/assets/js/061b9ec0.9c3a0854.js deleted file mode 100644 index 4fdc473150..0000000000 --- a/assets/js/061b9ec0.9c3a0854.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[74685],{3905:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>m});var n=r(67294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function s(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function a(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var l=n.createContext({}),c=function(e){var t=n.useContext(l),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},u=function(e){var t=c(e.components);return n.createElement(l.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,s=e.originalType,l=e.parentName,u=i(e,["components","mdxType","originalType","parentName"]),d=c(r),m=o,f=d["".concat(l,".").concat(m)]||d[m]||p[m]||s;return r?n.createElement(f,a(a({ref:t},u),{},{components:r})):n.createElement(f,a({ref:t},u))}));function m(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var s=r.length,a=new Array(s);a[0]=d;var i={};for(var l in t)hasOwnProperty.call(t,l)&&(i[l]=t[l]);i.originalType=e,i.mdxType="string"==typeof e?e:o,a[1]=i;for(var c=2;c{r.r(t),r.d(t,{assets:()=>u,contentTitle:()=>l,default:()=>m,frontMatter:()=>i,metadata:()=>c,toc:()=>p});var n=r(83117),o=r(80102),s=(r(67294),r(3905)),a=["components"],i={id:"css-guidelines",title:"CSS Guidelines",sidebar_label:"CSS Guidelines"},l=void 0,c={unversionedId:"css-guidelines",id:"version-2.21.1/css-guidelines",title:"CSS Guidelines",description:"The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.",source:"@site/versioned_docs/version-2.21.1/css-guidelines.md",sourceDirName:".",slug:"/css-guidelines",permalink:"/docs/css-guidelines",draft:!1,tags:[],version:"2.21.1",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1680624606,formattedLastUpdatedAt:"Apr 4, 2023",frontMatter:{id:"css-guidelines",title:"CSS Guidelines",sidebar_label:"CSS Guidelines"},sidebar:"docs",previous:{title:"Accessibility testing",permalink:"/docs/accessibility-testing"}},u={},p=[{value:"File structuration",id:"file-structuration",level:2},{value:"Code structuration",id:"code-structuration",level:2},{value:"Quick pointers",id:"quick-pointers",level:2}],d={toc:p};function m(e){var t=e.components,r=(0,o.Z)(e,a);return(0,s.kt)("wrapper",(0,n.Z)({},d,r,{components:t,mdxType:"MDXLayout"}),(0,s.kt)("p",null,"The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly."),(0,s.kt)("p",null,"Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations."),(0,s.kt)("h2",{id:"file-structuration"},"File structuration"),(0,s.kt)("p",null,"Rules should be placed where their purpose is most apparent, and where they are easiest to find."),(0,s.kt)("p",null,"Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a ",(0,s.kt)("inlineCode",{parentName:"p"},".scss")," file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are ",(0,s.kt)("strong",{parentName:"p"},"specific to this component/template and this one only")),(0,s.kt)("p",null,"Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central ",(0,s.kt)("inlineCode",{parentName:"p"},"app/static/scss")," folder."),(0,s.kt)("h2",{id:"code-structuration"},"Code structuration"),(0,s.kt)("p",null,"In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes."),(0,s.kt)("p",null,"Following the ",(0,s.kt)("a",{parentName:"p",href:"http://getbem.com/introduction/"},"BEM naming convention"),", we will write our classes as follows :"),(0,s.kt)("pre",null,(0,s.kt)("code",{parentName:"pre"},".block {}\n.block__element {}\n.block--modifier {}\n")),(0,s.kt)("ul",null,(0,s.kt)("li",{parentName:"ul"},(0,s.kt)("inlineCode",{parentName:"li"},".block")," represents the higher level of an abstraction or component."),(0,s.kt)("li",{parentName:"ul"},(0,s.kt)("inlineCode",{parentName:"li"},".block__element")," represents a descendent of .block that helps form .block as a whole."),(0,s.kt)("li",{parentName:"ul"},(0,s.kt)("inlineCode",{parentName:"li"},".block--modifier")," represents a different state or version of .block.")),(0,s.kt)("p",null,"We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. ",(0,s.kt)("inlineCode",{parentName:"p"},".site-search__field--full"),")."),(0,s.kt)("p",null,"Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup."),(0,s.kt)("h2",{id:"quick-pointers"},"Quick pointers"),(0,s.kt)("ul",null,(0,s.kt)("li",{parentName:"ul"},"In general, ",(0,s.kt)("strong",{parentName:"li"},"do not use IDs"),". ",(0,s.kt)("em",{parentName:"li"},"They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.")),(0,s.kt)("li",{parentName:"ul"},"Do not nest selectors unnecessarily. ",(0,s.kt)("em",{parentName:"li"},"It will increase specificity and affect where else you can use your styles.")),(0,s.kt)("li",{parentName:"ul"},"Do not qualify selectors unnecessarily. ",(0,s.kt)("em",{parentName:"li"},"It will impact the number of different elements you can apply styles to.")),(0,s.kt)("li",{parentName:"ul"},"Comment profusely, ",(0,s.kt)("em",{parentName:"li"},"whenever you think the CSS is getting complex or it would not be easy to understand its intent.")),(0,s.kt)("li",{parentName:"ul"},"Use !important proactively. ",(0,s.kt)("em",{parentName:"li"},"!important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively."))),(0,s.kt)("p",null,"(Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. ",(0,s.kt)("inlineCode",{parentName:"p"},"div > #example")," is A LOT more efficient than ",(0,s.kt)("inlineCode",{parentName:"p"},"#example > div")," although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See ",(0,s.kt)("a",{parentName:"p",href:"http://csswizardry.com/2011/09/writing-efficient-css-selectors/"},"CSS Wizardry")," for more details."))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/061b9ec0.bfb49803.js b/assets/js/061b9ec0.bfb49803.js new file mode 100644 index 0000000000..90d6b5b249 --- /dev/null +++ b/assets/js/061b9ec0.bfb49803.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[74685],{3905:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>m});var n=r(67294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function s(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function a(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var l=n.createContext({}),c=function(e){var t=n.useContext(l),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},u=function(e){var t=c(e.components);return n.createElement(l.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,s=e.originalType,l=e.parentName,u=i(e,["components","mdxType","originalType","parentName"]),d=c(r),m=o,f=d["".concat(l,".").concat(m)]||d[m]||p[m]||s;return r?n.createElement(f,a(a({ref:t},u),{},{components:r})):n.createElement(f,a({ref:t},u))}));function m(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var s=r.length,a=new Array(s);a[0]=d;var i={};for(var l in t)hasOwnProperty.call(t,l)&&(i[l]=t[l]);i.originalType=e,i.mdxType="string"==typeof e?e:o,a[1]=i;for(var c=2;c{r.r(t),r.d(t,{assets:()=>u,contentTitle:()=>l,default:()=>m,frontMatter:()=>i,metadata:()=>c,toc:()=>p});var n=r(83117),o=r(80102),s=(r(67294),r(3905)),a=["components"],i={id:"css-guidelines",title:"CSS Guidelines",sidebar_label:"CSS Guidelines"},l=void 0,c={unversionedId:"css-guidelines",id:"version-2.21.1/css-guidelines",title:"CSS Guidelines",description:"The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.",source:"@site/versioned_docs/version-2.21.1/css-guidelines.md",sourceDirName:".",slug:"/css-guidelines",permalink:"/docs/2.21.1/css-guidelines",draft:!1,tags:[],version:"2.21.1",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1680624606,formattedLastUpdatedAt:"Apr 4, 2023",frontMatter:{id:"css-guidelines",title:"CSS Guidelines",sidebar_label:"CSS Guidelines"},sidebar:"docs",previous:{title:"Accessibility testing",permalink:"/docs/2.21.1/accessibility-testing"}},u={},p=[{value:"File structuration",id:"file-structuration",level:2},{value:"Code structuration",id:"code-structuration",level:2},{value:"Quick pointers",id:"quick-pointers",level:2}],d={toc:p};function m(e){var t=e.components,r=(0,o.Z)(e,a);return(0,s.kt)("wrapper",(0,n.Z)({},d,r,{components:t,mdxType:"MDXLayout"}),(0,s.kt)("p",null,"The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly."),(0,s.kt)("p",null,"Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations."),(0,s.kt)("h2",{id:"file-structuration"},"File structuration"),(0,s.kt)("p",null,"Rules should be placed where their purpose is most apparent, and where they are easiest to find."),(0,s.kt)("p",null,"Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a ",(0,s.kt)("inlineCode",{parentName:"p"},".scss")," file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are ",(0,s.kt)("strong",{parentName:"p"},"specific to this component/template and this one only")),(0,s.kt)("p",null,"Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central ",(0,s.kt)("inlineCode",{parentName:"p"},"app/static/scss")," folder."),(0,s.kt)("h2",{id:"code-structuration"},"Code structuration"),(0,s.kt)("p",null,"In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes."),(0,s.kt)("p",null,"Following the ",(0,s.kt)("a",{parentName:"p",href:"http://getbem.com/introduction/"},"BEM naming convention"),", we will write our classes as follows :"),(0,s.kt)("pre",null,(0,s.kt)("code",{parentName:"pre"},".block {}\n.block__element {}\n.block--modifier {}\n")),(0,s.kt)("ul",null,(0,s.kt)("li",{parentName:"ul"},(0,s.kt)("inlineCode",{parentName:"li"},".block")," represents the higher level of an abstraction or component."),(0,s.kt)("li",{parentName:"ul"},(0,s.kt)("inlineCode",{parentName:"li"},".block__element")," represents a descendent of .block that helps form .block as a whole."),(0,s.kt)("li",{parentName:"ul"},(0,s.kt)("inlineCode",{parentName:"li"},".block--modifier")," represents a different state or version of .block.")),(0,s.kt)("p",null,"We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. ",(0,s.kt)("inlineCode",{parentName:"p"},".site-search__field--full"),")."),(0,s.kt)("p",null,"Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup."),(0,s.kt)("h2",{id:"quick-pointers"},"Quick pointers"),(0,s.kt)("ul",null,(0,s.kt)("li",{parentName:"ul"},"In general, ",(0,s.kt)("strong",{parentName:"li"},"do not use IDs"),". ",(0,s.kt)("em",{parentName:"li"},"They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.")),(0,s.kt)("li",{parentName:"ul"},"Do not nest selectors unnecessarily. ",(0,s.kt)("em",{parentName:"li"},"It will increase specificity and affect where else you can use your styles.")),(0,s.kt)("li",{parentName:"ul"},"Do not qualify selectors unnecessarily. ",(0,s.kt)("em",{parentName:"li"},"It will impact the number of different elements you can apply styles to.")),(0,s.kt)("li",{parentName:"ul"},"Comment profusely, ",(0,s.kt)("em",{parentName:"li"},"whenever you think the CSS is getting complex or it would not be easy to understand its intent.")),(0,s.kt)("li",{parentName:"ul"},"Use !important proactively. ",(0,s.kt)("em",{parentName:"li"},"!important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively."))),(0,s.kt)("p",null,"(Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. ",(0,s.kt)("inlineCode",{parentName:"p"},"div > #example")," is A LOT more efficient than ",(0,s.kt)("inlineCode",{parentName:"p"},"#example > div")," although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See ",(0,s.kt)("a",{parentName:"p",href:"http://csswizardry.com/2011/09/writing-efficient-css-selectors/"},"CSS Wizardry")," for more details."))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/0add6094.d6475b98.js b/assets/js/0add6094.d6475b98.js deleted file mode 100644 index e039c0e668..0000000000 --- a/assets/js/0add6094.d6475b98.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[70005],{3905:(e,n,t)=>{t.d(n,{Zo:()=>u,kt:()=>m});var r=t(67294);function o(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function i(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);n&&(r=r.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,r)}return t}function a(e){for(var n=1;n=0||(o[t]=e[t]);return o}(e,n);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}var l=r.createContext({}),c=function(e){var n=r.useContext(l),t=n;return e&&(t="function"==typeof e?e(n):a(a({},n),e)),t},u=function(e){var n=c(e.components);return r.createElement(l.Provider,{value:n},e.children)},p={inlineCode:"code",wrapper:function(e){var n=e.children;return r.createElement(r.Fragment,{},n)}},d=r.forwardRef((function(e,n){var t=e.components,o=e.mdxType,i=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),d=c(t),m=o,h=d["".concat(l,".").concat(m)]||d[m]||p[m]||i;return t?r.createElement(h,a(a({ref:n},u),{},{components:t})):r.createElement(h,a({ref:n},u))}));function m(e,n){var t=arguments,o=n&&n.mdxType;if("string"==typeof e||o){var i=t.length,a=new Array(i);a[0]=d;var s={};for(var l in n)hasOwnProperty.call(n,l)&&(s[l]=n[l]);s.originalType=e,s.mdxType="string"==typeof e?e:o,a[1]=s;for(var c=2;c{t.r(n),t.d(n,{assets:()=>u,contentTitle:()=>l,default:()=>m,frontMatter:()=>s,metadata:()=>c,toc:()=>p});var r=t(83117),o=t(80102),i=(t(67294),t(3905)),a=["components"],s={id:"course-run-synchronization-api",title:"Course run synchronization API",sidebar_label:"course run sync"},l=void 0,c={unversionedId:"api/course-run-synchronization-api",id:"version-2.21.1/api/course-run-synchronization-api",title:"Course run synchronization API",description:"API endpoint allowing remote systems to synchronize their course runs with a Richie instance.",source:"@site/versioned_docs/version-2.21.1/api/course-run-synchronization-api.md",sourceDirName:"api",slug:"/api/course-run-synchronization-api",permalink:"/docs/api/course-run-synchronization-api",draft:!1,tags:[],version:"2.21.1",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1680624606,formattedLastUpdatedAt:"Apr 4, 2023",frontMatter:{id:"course-run-synchronization-api",title:"Course run synchronization API",sidebar_label:"course run sync"}},u={},p=[{value:"Synchronization endpoint /api/1.0/course-runs-sync",id:"synchronization-endpoint-api10course-runs-sync",level:2},{value:"Synchronize a course run POST",id:"synchronize-a-course-run-post",level:3}],d={toc:p};function m(e){var n=e.components,t=(0,o.Z)(e,a);return(0,i.kt)("wrapper",(0,r.Z)({},d,t,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"API endpoint allowing remote systems to synchronize their course runs with a Richie instance."),(0,i.kt)("h2",{id:"synchronization-endpoint-api10course-runs-sync"},"Synchronization endpoint ","[/api/1.0/course-runs-sync]"),(0,i.kt)("p",null,'This documentation describes version "1.0" of this API endpoint.'),(0,i.kt)("h3",{id:"synchronize-a-course-run-post"},"Synchronize a course run ","[POST]"),(0,i.kt)("p",null,"It takes a JSON object containing the course run details:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"resource_link: ",(0,i.kt)("inlineCode",{parentName:"li"},"https://lms.example.com/courses/course-v1:001+001+001/info")," (string, required) -\nurl of the course syllabus on the LMS from which a unique course identifier can be extracted"),(0,i.kt)("li",{parentName:"ul"},"start: ",(0,i.kt)("inlineCode",{parentName:"li"},"2018-02-01T06:00:00Z")," (string, optional) - ISO 8601 date, when this session of the\ncourse starts"),(0,i.kt)("li",{parentName:"ul"},"end: ",(0,i.kt)("inlineCode",{parentName:"li"},"2018-02-28T06:00:00Z")," (string, optional) - ISO 8601 date, when this session of the course\nends"),(0,i.kt)("li",{parentName:"ul"},"enrollment_start: ",(0,i.kt)("inlineCode",{parentName:"li"},"2018-01-01T06:00:00Z")," (string, optional) - ISO 8601 date, when enrollment\nfor this session of the course starts"),(0,i.kt)("li",{parentName:"ul"},"enrollment_end: ",(0,i.kt)("inlineCode",{parentName:"li"},"2018-01-31T06:00:00Z")," (string, optional) - ISO 8601 date, when enrollment for\nthis session of the course ends"),(0,i.kt)("li",{parentName:"ul"},"languages: ","['fr', 'en']"," (array","[string]",", required) - ISO 639-1 code (2 letters) for the course's\nlanguages")),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"Request (application/json)"),(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"Headers"),(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"Authorization: ",(0,i.kt)("inlineCode",{parentName:"li"},"SIG-HMAC-SHA256 xxxxxxx")," (string, required) - Authorization header\ncontaining the digest of the utf-8 encoded json representation of the submitted data\nfor the given secret key and SHA256 digest algorithm (see ","[synchronizing-course-runs]","\nfor an example)."))),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"Body"),(0,i.kt)("pre",{parentName:"li"},(0,i.kt)("code",{parentName:"pre"},' {\n "resource_link": "https://lms.example.com/courses/course-v1:001+001+001/info",\n "start": "2021-02-01T00:00:00Z",\n "end": "2021-02-31T23:59:59Z",\n "enrollment_start": "2021-01-01T00:00:00Z",\n "enrollment_end": "2021-01-31T23:59:59Z",\n "languages": ["en", "fr"]\n }\n'))))),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"Response 200 (application/json)"),(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"Body"),(0,i.kt)("pre",{parentName:"li"},(0,i.kt)("code",{parentName:"pre"},' {\n "success": True\n }\n')))))))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/0add6094.ee28a2c0.js b/assets/js/0add6094.ee28a2c0.js new file mode 100644 index 0000000000..6777f84b37 --- /dev/null +++ b/assets/js/0add6094.ee28a2c0.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[70005],{3905:(e,n,t)=>{t.d(n,{Zo:()=>u,kt:()=>m});var r=t(67294);function o(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function i(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);n&&(r=r.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,r)}return t}function a(e){for(var n=1;n=0||(o[t]=e[t]);return o}(e,n);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}var l=r.createContext({}),c=function(e){var n=r.useContext(l),t=n;return e&&(t="function"==typeof e?e(n):a(a({},n),e)),t},u=function(e){var n=c(e.components);return r.createElement(l.Provider,{value:n},e.children)},p={inlineCode:"code",wrapper:function(e){var n=e.children;return r.createElement(r.Fragment,{},n)}},d=r.forwardRef((function(e,n){var t=e.components,o=e.mdxType,i=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),d=c(t),m=o,h=d["".concat(l,".").concat(m)]||d[m]||p[m]||i;return t?r.createElement(h,a(a({ref:n},u),{},{components:t})):r.createElement(h,a({ref:n},u))}));function m(e,n){var t=arguments,o=n&&n.mdxType;if("string"==typeof e||o){var i=t.length,a=new Array(i);a[0]=d;var s={};for(var l in n)hasOwnProperty.call(n,l)&&(s[l]=n[l]);s.originalType=e,s.mdxType="string"==typeof e?e:o,a[1]=s;for(var c=2;c{t.r(n),t.d(n,{assets:()=>u,contentTitle:()=>l,default:()=>m,frontMatter:()=>s,metadata:()=>c,toc:()=>p});var r=t(83117),o=t(80102),i=(t(67294),t(3905)),a=["components"],s={id:"course-run-synchronization-api",title:"Course run synchronization API",sidebar_label:"course run sync"},l=void 0,c={unversionedId:"api/course-run-synchronization-api",id:"version-2.21.1/api/course-run-synchronization-api",title:"Course run synchronization API",description:"API endpoint allowing remote systems to synchronize their course runs with a Richie instance.",source:"@site/versioned_docs/version-2.21.1/api/course-run-synchronization-api.md",sourceDirName:"api",slug:"/api/course-run-synchronization-api",permalink:"/docs/2.21.1/api/course-run-synchronization-api",draft:!1,tags:[],version:"2.21.1",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1680624606,formattedLastUpdatedAt:"Apr 4, 2023",frontMatter:{id:"course-run-synchronization-api",title:"Course run synchronization API",sidebar_label:"course run sync"}},u={},p=[{value:"Synchronization endpoint /api/1.0/course-runs-sync",id:"synchronization-endpoint-api10course-runs-sync",level:2},{value:"Synchronize a course run POST",id:"synchronize-a-course-run-post",level:3}],d={toc:p};function m(e){var n=e.components,t=(0,o.Z)(e,a);return(0,i.kt)("wrapper",(0,r.Z)({},d,t,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"API endpoint allowing remote systems to synchronize their course runs with a Richie instance."),(0,i.kt)("h2",{id:"synchronization-endpoint-api10course-runs-sync"},"Synchronization endpoint ","[/api/1.0/course-runs-sync]"),(0,i.kt)("p",null,'This documentation describes version "1.0" of this API endpoint.'),(0,i.kt)("h3",{id:"synchronize-a-course-run-post"},"Synchronize a course run ","[POST]"),(0,i.kt)("p",null,"It takes a JSON object containing the course run details:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"resource_link: ",(0,i.kt)("inlineCode",{parentName:"li"},"https://lms.example.com/courses/course-v1:001+001+001/info")," (string, required) -\nurl of the course syllabus on the LMS from which a unique course identifier can be extracted"),(0,i.kt)("li",{parentName:"ul"},"start: ",(0,i.kt)("inlineCode",{parentName:"li"},"2018-02-01T06:00:00Z")," (string, optional) - ISO 8601 date, when this session of the\ncourse starts"),(0,i.kt)("li",{parentName:"ul"},"end: ",(0,i.kt)("inlineCode",{parentName:"li"},"2018-02-28T06:00:00Z")," (string, optional) - ISO 8601 date, when this session of the course\nends"),(0,i.kt)("li",{parentName:"ul"},"enrollment_start: ",(0,i.kt)("inlineCode",{parentName:"li"},"2018-01-01T06:00:00Z")," (string, optional) - ISO 8601 date, when enrollment\nfor this session of the course starts"),(0,i.kt)("li",{parentName:"ul"},"enrollment_end: ",(0,i.kt)("inlineCode",{parentName:"li"},"2018-01-31T06:00:00Z")," (string, optional) - ISO 8601 date, when enrollment for\nthis session of the course ends"),(0,i.kt)("li",{parentName:"ul"},"languages: ","['fr', 'en']"," (array","[string]",", required) - ISO 639-1 code (2 letters) for the course's\nlanguages")),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"Request (application/json)"),(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"Headers"),(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"Authorization: ",(0,i.kt)("inlineCode",{parentName:"li"},"SIG-HMAC-SHA256 xxxxxxx")," (string, required) - Authorization header\ncontaining the digest of the utf-8 encoded json representation of the submitted data\nfor the given secret key and SHA256 digest algorithm (see ","[synchronizing-course-runs]","\nfor an example)."))),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"Body"),(0,i.kt)("pre",{parentName:"li"},(0,i.kt)("code",{parentName:"pre"},' {\n "resource_link": "https://lms.example.com/courses/course-v1:001+001+001/info",\n "start": "2021-02-01T00:00:00Z",\n "end": "2021-02-31T23:59:59Z",\n "enrollment_start": "2021-01-01T00:00:00Z",\n "enrollment_end": "2021-01-31T23:59:59Z",\n "languages": ["en", "fr"]\n }\n'))))),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"Response 200 (application/json)"),(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"Body"),(0,i.kt)("pre",{parentName:"li"},(0,i.kt)("code",{parentName:"pre"},' {\n "success": True\n }\n')))))))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/0b704f48.a57f152d.js b/assets/js/0b704f48.a57f152d.js deleted file mode 100644 index 1eb31f4bb0..0000000000 --- a/assets/js/0b704f48.a57f152d.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[43382],{3905:(e,t,o)=>{o.d(t,{Zo:()=>s,kt:()=>h});var r=o(67294);function n(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function i(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,r)}return o}function a(e){for(var t=1;t=0||(n[o]=e[o]);return n}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,o)&&(n[o]=e[o])}return n}var l=r.createContext({}),u=function(e){var t=r.useContext(l),o=t;return e&&(o="function"==typeof e?e(t):a(a({},t),e)),o},s=function(e){var t=u(e.components);return r.createElement(l.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var o=e.components,n=e.mdxType,i=e.originalType,l=e.parentName,s=c(e,["components","mdxType","originalType","parentName"]),d=u(o),h=n,m=d["".concat(l,".").concat(h)]||d[h]||p[h]||i;return o?r.createElement(m,a(a({ref:t},s),{},{components:o})):r.createElement(m,a({ref:t},s))}));function h(e,t){var o=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var i=o.length,a=new Array(i);a[0]=d;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c.mdxType="string"==typeof e?e:n,a[1]=c;for(var u=2;u{o.r(t),o.d(t,{assets:()=>s,contentTitle:()=>l,default:()=>h,frontMatter:()=>c,metadata:()=>u,toc:()=>p});var r=o(83117),n=o(80102),i=(o(67294),o(3905)),a=["components"],c={id:"cookiecutter",title:"Start your own site",sidebar_label:"Start your own site"},l=void 0,u={unversionedId:"cookiecutter",id:"cookiecutter",title:"Start your own site",description:"We use Cookiecutter to help you",source:"@site/../docs/cookiecutter.md",sourceDirName:".",slug:"/cookiecutter",permalink:"/docs/next/cookiecutter",draft:!1,tags:[],version:"current",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1680624606,formattedLastUpdatedAt:"Apr 4, 2023",frontMatter:{id:"cookiecutter",title:"Start your own site",sidebar_label:"Start your own site"},sidebar:"docs",previous:{title:"Discover Richie",permalink:"/docs/next/discover"},next:{title:"Search filters customization",permalink:"/docs/next/filters-customization"}},s={},p=[{value:"Run Cookiecutter",id:"run-cookiecutter",level:2},{value:"Bootstrap your project",id:"bootstrap-your-project",level:3},{value:"Theming",id:"theming",level:2},{value:"Update your Richie site factory",id:"update-your-richie-site-factory",level:2},{value:"Help us improve this project",id:"help-us-improve-this-project",level:2}],d={toc:p};function h(e){var t=e.components,o=(0,n.Z)(e,a);return(0,i.kt)("wrapper",(0,r.Z)({},d,o,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"We use ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/audreyr/cookiecutter"},"Cookiecutter")," to help you\nset up a production-ready learning portal website based on\n",(0,i.kt)("a",{parentName:"p",href:"https://github.com/openfun/richie"},"Richie")," in seconds."),(0,i.kt)("h2",{id:"run-cookiecutter"},"Run Cookiecutter"),(0,i.kt)("p",null,"There are 2 options to run Cookiecutter:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://cookiecutter.readthedocs.io/en/latest/installation.html"},"install it on your machine")),(0,i.kt)("li",{parentName:"ul"},"run it with Docker")),(0,i.kt)("p",null,"While you think of it, navigate to the directory in which you want to create\nyour site factory:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"cd /path/to/your/code/directory\n")),(0,i.kt)("p",null,"If you chose to install Cookiecutter, you can now run it against our\n",(0,i.kt)("a",{parentName:"p",href:"https://github.com/openfun/richie/tree/master/cookiecutter"},"template")," as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"cookiecutter gh:openfun/richie --directory cookiecutter --checkout v2.21.1\n")),(0,i.kt)("p",null,"If you didn't want to install it on your machine, we provide a Docker image\nbuilt with our ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/openfun/dockerfiles"},"own repository")," that you can use as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"docker run --rm -it -u $(id -u):$(id -g) -v $PWD:/app \\\nfundocker/cookiecutter gh:openfun/richie --directory cookiecutter --checkout v2.21.1\n")),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"--directory")," option is to indicate that our Cookiecutter template is in\na ",(0,i.kt)("inlineCode",{parentName:"p"},"cookiecutter")," directory inside our git repository and not at the root."),(0,i.kt)("p",null,'You will be prompted to enter an organization name, which will determine the\nname of your repository. For example, if you choose "foo" as organization\nname, your repository will be named ',(0,i.kt)("inlineCode",{parentName:"p"},"foo-richie-site-factory"),". It's\nnice if you keep it that way so all richie site factories follow this\nconvention."),(0,i.kt)("p",null,"When you confirm the organization name, Cookiecutter will generate your\nproject from the Cookiecutter template and place it at the level where you\ncurrently are."),(0,i.kt)("h3",{id:"bootstrap-your-project"},"Bootstrap your project"),(0,i.kt)("p",null,"Enter the newly created project and add a new site to your site factory:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"cd foo-richie-site-factory\nmake add-site\n")),(0,i.kt)("p",null,"This script also uses Cookiecutter against our ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/openfun/richie/tree/master/cookiecutter/%7B%7Bcookiecutter.organization%7D%7D-richie-site-factory/template"},"site template"),"."),(0,i.kt)("p",null,"Once your new site is created, activate it:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"bin/activate\n")),(0,i.kt)("p",null,"Now bootstrap the site to build its docker image, create its media folder,\ndatabase, etc.:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"make bootstrap\n")),(0,i.kt)("p",null,"Once the bootstrap phase is finished, you should be able to view the site at\n",(0,i.kt)("a",{parentName:"p",href:"http://localhost:8070"},"localhost:8070"),"."),(0,i.kt)("p",null,"You can create a full fledge demo to test your site by running:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"make demo-site\n")),(0,i.kt)("p",null,"Note that the README of your newly created site factory contains detailed\ninformation about how to configure and run a site."),(0,i.kt)("p",null,"Once you're happy with your site, don't forget to backup your work e.g. by\ncommitting it and pushing it to a new git repository."),(0,i.kt)("h2",{id:"theming"},"Theming"),(0,i.kt)("p",null,"You probably want to change the default theme. The cookiecutter adds an extra scss frontend folder with a couple of templates that you can use to change the default styling of the site."),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"sites//src/frontend/scss/extras/colors/_palette.scss")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"sites//src/frontend/scss/extras/colors/_theme.scss"))),(0,i.kt)("p",null,"To change the default logo of the site, you need to create the folder ",(0,i.kt)("inlineCode",{parentName:"p"},"sites//src/backend/base/static/richie/images")," and then override the new ",(0,i.kt)("inlineCode",{parentName:"p"},"logo.png")," picture."),(0,i.kt)("p",null,"For more advanced customization, refer to our recipes:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/docs/next/filters-customization"},"How to customize search filters")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/docs/next/frontend-overrides"},"How to override frontend components in Richie"))),(0,i.kt)("h2",{id:"update-your-richie-site-factory"},"Update your Richie site factory"),(0,i.kt)("p",null,"If we later improve our scripts, you will be able to update your own site\nfactory by replaying Cookiecutter. This will override your files in the\nproject's scaffolding but, don't worry, it will respect all the sites you\nwill have created in the ",(0,i.kt)("inlineCode",{parentName:"p"},"sites")," directory:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"cookiecutter --overwrite-if-exists gh:openfun/richie --directory=cookiecutter\n")),(0,i.kt)("h2",{id:"help-us-improve-this-project"},"Help us improve this project"),(0,i.kt)("p",null,"After starting your project, please submit an issue let us know how it went and\nwhat other features we should add to make it better."))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/0b704f48.a65ce7f2.js b/assets/js/0b704f48.a65ce7f2.js new file mode 100644 index 0000000000..567b567cbb --- /dev/null +++ b/assets/js/0b704f48.a65ce7f2.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[43382],{3905:(e,t,o)=>{o.d(t,{Zo:()=>s,kt:()=>h});var r=o(67294);function n(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function i(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,r)}return o}function a(e){for(var t=1;t=0||(n[o]=e[o]);return n}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,o)&&(n[o]=e[o])}return n}var l=r.createContext({}),u=function(e){var t=r.useContext(l),o=t;return e&&(o="function"==typeof e?e(t):a(a({},t),e)),o},s=function(e){var t=u(e.components);return r.createElement(l.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var o=e.components,n=e.mdxType,i=e.originalType,l=e.parentName,s=c(e,["components","mdxType","originalType","parentName"]),d=u(o),h=n,m=d["".concat(l,".").concat(h)]||d[h]||p[h]||i;return o?r.createElement(m,a(a({ref:t},s),{},{components:o})):r.createElement(m,a({ref:t},s))}));function h(e,t){var o=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var i=o.length,a=new Array(i);a[0]=d;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c.mdxType="string"==typeof e?e:n,a[1]=c;for(var u=2;u{o.r(t),o.d(t,{assets:()=>s,contentTitle:()=>l,default:()=>h,frontMatter:()=>c,metadata:()=>u,toc:()=>p});var r=o(83117),n=o(80102),i=(o(67294),o(3905)),a=["components"],c={id:"cookiecutter",title:"Start your own site",sidebar_label:"Start your own site"},l=void 0,u={unversionedId:"cookiecutter",id:"cookiecutter",title:"Start your own site",description:"We use Cookiecutter to help you",source:"@site/../docs/cookiecutter.md",sourceDirName:".",slug:"/cookiecutter",permalink:"/docs/next/cookiecutter",draft:!1,tags:[],version:"current",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1682069684,formattedLastUpdatedAt:"Apr 21, 2023",frontMatter:{id:"cookiecutter",title:"Start your own site",sidebar_label:"Start your own site"},sidebar:"docs",previous:{title:"Discover Richie",permalink:"/docs/next/discover"},next:{title:"Search filters customization",permalink:"/docs/next/filters-customization"}},s={},p=[{value:"Run Cookiecutter",id:"run-cookiecutter",level:2},{value:"Bootstrap your project",id:"bootstrap-your-project",level:3},{value:"Theming",id:"theming",level:2},{value:"Update your Richie site factory",id:"update-your-richie-site-factory",level:2},{value:"Help us improve this project",id:"help-us-improve-this-project",level:2}],d={toc:p};function h(e){var t=e.components,o=(0,n.Z)(e,a);return(0,i.kt)("wrapper",(0,r.Z)({},d,o,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"We use ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/audreyr/cookiecutter"},"Cookiecutter")," to help you\nset up a production-ready learning portal website based on\n",(0,i.kt)("a",{parentName:"p",href:"https://github.com/openfun/richie"},"Richie")," in seconds."),(0,i.kt)("h2",{id:"run-cookiecutter"},"Run Cookiecutter"),(0,i.kt)("p",null,"There are 2 options to run Cookiecutter:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://cookiecutter.readthedocs.io/en/latest/installation.html"},"install it on your machine")),(0,i.kt)("li",{parentName:"ul"},"run it with Docker")),(0,i.kt)("p",null,"While you think of it, navigate to the directory in which you want to create\nyour site factory:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"cd /path/to/your/code/directory\n")),(0,i.kt)("p",null,"If you chose to install Cookiecutter, you can now run it against our\n",(0,i.kt)("a",{parentName:"p",href:"https://github.com/openfun/richie/tree/master/cookiecutter"},"template")," as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"cookiecutter gh:openfun/richie --directory cookiecutter --checkout v2.22.0\n")),(0,i.kt)("p",null,"If you didn't want to install it on your machine, we provide a Docker image\nbuilt with our ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/openfun/dockerfiles"},"own repository")," that you can use as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"docker run --rm -it -u $(id -u):$(id -g) -v $PWD:/app \\\nfundocker/cookiecutter gh:openfun/richie --directory cookiecutter --checkout v2.22.0\n")),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"--directory")," option is to indicate that our Cookiecutter template is in\na ",(0,i.kt)("inlineCode",{parentName:"p"},"cookiecutter")," directory inside our git repository and not at the root."),(0,i.kt)("p",null,'You will be prompted to enter an organization name, which will determine the\nname of your repository. For example, if you choose "foo" as organization\nname, your repository will be named ',(0,i.kt)("inlineCode",{parentName:"p"},"foo-richie-site-factory"),". It's\nnice if you keep it that way so all richie site factories follow this\nconvention."),(0,i.kt)("p",null,"When you confirm the organization name, Cookiecutter will generate your\nproject from the Cookiecutter template and place it at the level where you\ncurrently are."),(0,i.kt)("h3",{id:"bootstrap-your-project"},"Bootstrap your project"),(0,i.kt)("p",null,"Enter the newly created project and add a new site to your site factory:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"cd foo-richie-site-factory\nmake add-site\n")),(0,i.kt)("p",null,"This script also uses Cookiecutter against our ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/openfun/richie/tree/master/cookiecutter/%7B%7Bcookiecutter.organization%7D%7D-richie-site-factory/template"},"site template"),"."),(0,i.kt)("p",null,"Once your new site is created, activate it:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"bin/activate\n")),(0,i.kt)("p",null,"Now bootstrap the site to build its docker image, create its media folder,\ndatabase, etc.:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"make bootstrap\n")),(0,i.kt)("p",null,"Once the bootstrap phase is finished, you should be able to view the site at\n",(0,i.kt)("a",{parentName:"p",href:"http://localhost:8070"},"localhost:8070"),"."),(0,i.kt)("p",null,"You can create a full fledge demo to test your site by running:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"make demo-site\n")),(0,i.kt)("p",null,"Note that the README of your newly created site factory contains detailed\ninformation about how to configure and run a site."),(0,i.kt)("p",null,"Once you're happy with your site, don't forget to backup your work e.g. by\ncommitting it and pushing it to a new git repository."),(0,i.kt)("h2",{id:"theming"},"Theming"),(0,i.kt)("p",null,"You probably want to change the default theme. The cookiecutter adds an extra scss frontend folder with a couple of templates that you can use to change the default styling of the site."),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"sites//src/frontend/scss/extras/colors/_palette.scss")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"sites//src/frontend/scss/extras/colors/_theme.scss"))),(0,i.kt)("p",null,"To change the default logo of the site, you need to create the folder ",(0,i.kt)("inlineCode",{parentName:"p"},"sites//src/backend/base/static/richie/images")," and then override the new ",(0,i.kt)("inlineCode",{parentName:"p"},"logo.png")," picture."),(0,i.kt)("p",null,"For more advanced customization, refer to our recipes:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/docs/next/filters-customization"},"How to customize search filters")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/docs/next/frontend-overrides"},"How to override frontend components in Richie"))),(0,i.kt)("h2",{id:"update-your-richie-site-factory"},"Update your Richie site factory"),(0,i.kt)("p",null,"If we later improve our scripts, you will be able to update your own site\nfactory by replaying Cookiecutter. This will override your files in the\nproject's scaffolding but, don't worry, it will respect all the sites you\nwill have created in the ",(0,i.kt)("inlineCode",{parentName:"p"},"sites")," directory:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"cookiecutter --overwrite-if-exists gh:openfun/richie --directory=cookiecutter\n")),(0,i.kt)("h2",{id:"help-us-improve-this-project"},"Help us improve this project"),(0,i.kt)("p",null,"After starting your project, please submit an issue let us know how it went and\nwhat other features we should add to make it better."))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/0d621e61.706236e8.js b/assets/js/0d621e61.706236e8.js new file mode 100644 index 0000000000..eae4273258 --- /dev/null +++ b/assets/js/0d621e61.706236e8.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[33444],{3905:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>m});var a=n(67294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var s=a.createContext({}),p=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},c=function(e){var t=p(e.components);return a.createElement(s.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},d=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,s=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),d=p(n),m=r,k=d["".concat(s,".").concat(m)]||d[m]||u[m]||o;return n?a.createElement(k,i(i({ref:t},c),{},{components:n})):a.createElement(k,i({ref:t},c))}));function m(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,i=new Array(o);i[0]=d;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:r,i[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>m,frontMatter:()=>l,metadata:()=>p,toc:()=>u});var a=n(83117),r=n(80102),o=(n(67294),n(3905)),i=["components"],l={id:"installation",title:"Installing Richie for development",sidebar_label:"Installation"},s=void 0,p={unversionedId:"installation",id:"version-2.21.1/installation",title:"Installing Richie for development",description:"Richie is a container-native application but can also be installed",source:"@site/versioned_docs/version-2.21.1/installation.md",sourceDirName:".",slug:"/installation",permalink:"/docs/2.21.1/installation",draft:!1,tags:[],version:"2.21.1",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1680624606,formattedLastUpdatedAt:"Apr 4, 2023",frontMatter:{id:"installation",title:"Installing Richie for development",sidebar_label:"Installation"},sidebar:"docs",previous:{title:"Web Analytics",permalink:"/docs/2.21.1/web-analytics"},next:{title:"Docker development",permalink:"/docs/2.21.1/docker-development"}},c={},u=[{value:"Docker",id:"docker",level:2},{value:"Project bootstrap",id:"project-bootstrap",level:3},{value:"Adding content",id:"adding-content",level:3},{value:"Connecting Richie to an LMS",id:"connecting-richie-to-an-lms",level:2}],d={toc:u};function m(e){var t=e.components,n=(0,r.Z)(e,i);return(0,o.kt)("wrapper",(0,a.Z)({},d,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,(0,o.kt)("inlineCode",{parentName:"p"},"Richie")," is a ",(0,o.kt)("strong",{parentName:"p"},"container-native application")," but can also be installed\n",(0,o.kt)("a",{parentName:"p",href:"/docs/2.21.1/native-installation"},"on your machine"),"."),(0,o.kt)("p",null,"For development, the project is defined using a\n",(0,o.kt)("a",{parentName:"p",href:"../docker-compose.yml"},"docker-compose file")," and consists of:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("p",{parentName:"li"},"3 running services:"),(0,o.kt)("ul",{parentName:"li"},(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"database"),": ",(0,o.kt)("inlineCode",{parentName:"li"},"postgresql")," or ",(0,o.kt)("inlineCode",{parentName:"li"},"mysql")," at your preference,"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"elasticsearch"),": the search engine,"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"app"),": the actual ",(0,o.kt)("inlineCode",{parentName:"li"},"DjangoCMS")," project with all our application code."))),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("p",{parentName:"li"},"2 containers for building purposes:"),(0,o.kt)("ul",{parentName:"li"},(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"node"),": used for front-end related tasks, ",(0,o.kt)("em",{parentName:"li"},"i.e.")," transpiling\n",(0,o.kt)("inlineCode",{parentName:"li"},"TypeScript")," sources, bundling them into a JS package, and building the\nCSS files from Sass sources,"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"crowdin"),": used to upload and retrieve i18n files to and from the\n",(0,o.kt)("a",{parentName:"li",href:"https://crowdin.com/"},"Crowding")," service that we use to crowd source\ntranslations,")))),(0,o.kt)("p",null,'At "France Universit\xe9 Num\xe9rique", we deploy our applications on ',(0,o.kt)("inlineCode",{parentName:"p"},"Kubernetes"),"\nusing ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/openfun/arnold"},(0,o.kt)("inlineCode",{parentName:"a"},"Arnold")),"."),(0,o.kt)("h2",{id:"docker"},"Docker"),(0,o.kt)("p",null,"First, make sure you have a recent version of Docker and\n",(0,o.kt)("a",{parentName:"p",href:"https://docs.docker.com/compose/install"},"Docker Compose")," installed on your\nlaptop:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"$ docker -v\n Docker version 20.10.12, build e91ed57\n\n$ docker-compose --version\n docker-compose version 1.29.2, build 5becea4c\n")),(0,o.kt)("p",null,"\u26a0\ufe0f You may need to run the following commands with ",(0,o.kt)("inlineCode",{parentName:"p"},"sudo")," but this can be\navoided by assigning your user to the ",(0,o.kt)("inlineCode",{parentName:"p"},"docker")," group."),(0,o.kt)("h3",{id:"project-bootstrap"},"Project bootstrap"),(0,o.kt)("p",null,"The easiest way to start working on the project is to use our ",(0,o.kt)("inlineCode",{parentName:"p"},"Makefile"),":"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"$ make bootstrap\n")),(0,o.kt)("p",null,"This command builds the ",(0,o.kt)("inlineCode",{parentName:"p"},"app")," container, installs front-end and back-end\ndependencies, builds the front-end application and styles, and performs\ndatabase migrations. It's a good idea to use this command each time you are\npulling code from the project repository to avoid dependency-related or\nmigration-related issues."),(0,o.kt)("p",null,"Now that your ",(0,o.kt)("inlineCode",{parentName:"p"},"Docker")," services are ready to be used, start the full CMS by\nrunning:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"$ make run\n")),(0,o.kt)("h3",{id:"adding-content"},"Adding content"),(0,o.kt)("p",null,"Once the CMS is up and running, you can create a superuser account:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"$ make superuser\n")),(0,o.kt)("p",null,"You can create a basic demo site by running:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"$ make demo-site\n")),(0,o.kt)("p",null,"Note that if you don't create the demo site and start from a blank CMS, you\nwill get some errors requesting you to create some required root pages. So it\nis easier as a first approach to test the CMS with the demo site."),(0,o.kt)("p",null,"You should be able to view the site at ",(0,o.kt)("a",{parentName:"p",href:"http://localhost:8070"},"localhost:8070")),(0,o.kt)("h2",{id:"connecting-richie-to-an-lms"},"Connecting Richie to an LMS"),(0,o.kt)("p",null,"It is possible to use Richie as a catalogue aggregating courses from one or\nmore LMS without any specific connection. In this case, each course run in\nthe catalogue points to a course on the LMS, and the LMS points back to the\ncatalogue to browse courses."),(0,o.kt)("p",null,"This approach is used for example on ",(0,o.kt)("a",{parentName:"p",href:"https://www.fun-campus.fr"},"https://www.fun-campus.fr")," or\n",(0,o.kt)("a",{parentName:"p",href:"https://catalogue.edulib.org"},"https://catalogue.edulib.org"),"."),(0,o.kt)("p",null,"For a seamless user experience, it is possible to connect a Richie instance\nto an OpenEdX instance (or some other LMS like Moodle at the cost of minor\nadaptations), in several ways that we explain in the\n",(0,o.kt)("a",{parentName:"p",href:"lms-connection"},"LMS connection guide"),"."),(0,o.kt)("p",null,"This approach is used for example on ",(0,o.kt)("a",{parentName:"p",href:"https://www.fun-mooc.fr"},"https://www.fun-mooc.fr")," or\n",(0,o.kt)("a",{parentName:"p",href:"https://www.nau.edu.pt"},"https://www.nau.edu.pt"),"."))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/0d621e61.cd820561.js b/assets/js/0d621e61.cd820561.js deleted file mode 100644 index a83528df8a..0000000000 --- a/assets/js/0d621e61.cd820561.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[33444],{3905:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>m});var a=n(67294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var s=a.createContext({}),p=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},c=function(e){var t=p(e.components);return a.createElement(s.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},d=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,s=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),d=p(n),m=r,k=d["".concat(s,".").concat(m)]||d[m]||u[m]||o;return n?a.createElement(k,i(i({ref:t},c),{},{components:n})):a.createElement(k,i({ref:t},c))}));function m(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,i=new Array(o);i[0]=d;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:r,i[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>m,frontMatter:()=>l,metadata:()=>p,toc:()=>u});var a=n(83117),r=n(80102),o=(n(67294),n(3905)),i=["components"],l={id:"installation",title:"Installing Richie for development",sidebar_label:"Installation"},s=void 0,p={unversionedId:"installation",id:"version-2.21.1/installation",title:"Installing Richie for development",description:"Richie is a container-native application but can also be installed",source:"@site/versioned_docs/version-2.21.1/installation.md",sourceDirName:".",slug:"/installation",permalink:"/docs/installation",draft:!1,tags:[],version:"2.21.1",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1680624606,formattedLastUpdatedAt:"Apr 4, 2023",frontMatter:{id:"installation",title:"Installing Richie for development",sidebar_label:"Installation"},sidebar:"docs",previous:{title:"Web Analytics",permalink:"/docs/web-analytics"},next:{title:"Docker development",permalink:"/docs/docker-development"}},c={},u=[{value:"Docker",id:"docker",level:2},{value:"Project bootstrap",id:"project-bootstrap",level:3},{value:"Adding content",id:"adding-content",level:3},{value:"Connecting Richie to an LMS",id:"connecting-richie-to-an-lms",level:2}],d={toc:u};function m(e){var t=e.components,n=(0,r.Z)(e,i);return(0,o.kt)("wrapper",(0,a.Z)({},d,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,(0,o.kt)("inlineCode",{parentName:"p"},"Richie")," is a ",(0,o.kt)("strong",{parentName:"p"},"container-native application")," but can also be installed\n",(0,o.kt)("a",{parentName:"p",href:"/docs/native-installation"},"on your machine"),"."),(0,o.kt)("p",null,"For development, the project is defined using a\n",(0,o.kt)("a",{parentName:"p",href:"../docker-compose.yml"},"docker-compose file")," and consists of:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("p",{parentName:"li"},"3 running services:"),(0,o.kt)("ul",{parentName:"li"},(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"database"),": ",(0,o.kt)("inlineCode",{parentName:"li"},"postgresql")," or ",(0,o.kt)("inlineCode",{parentName:"li"},"mysql")," at your preference,"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"elasticsearch"),": the search engine,"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"app"),": the actual ",(0,o.kt)("inlineCode",{parentName:"li"},"DjangoCMS")," project with all our application code."))),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("p",{parentName:"li"},"2 containers for building purposes:"),(0,o.kt)("ul",{parentName:"li"},(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"node"),": used for front-end related tasks, ",(0,o.kt)("em",{parentName:"li"},"i.e.")," transpiling\n",(0,o.kt)("inlineCode",{parentName:"li"},"TypeScript")," sources, bundling them into a JS package, and building the\nCSS files from Sass sources,"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"crowdin"),": used to upload and retrieve i18n files to and from the\n",(0,o.kt)("a",{parentName:"li",href:"https://crowdin.com/"},"Crowding")," service that we use to crowd source\ntranslations,")))),(0,o.kt)("p",null,'At "France Universit\xe9 Num\xe9rique", we deploy our applications on ',(0,o.kt)("inlineCode",{parentName:"p"},"Kubernetes"),"\nusing ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/openfun/arnold"},(0,o.kt)("inlineCode",{parentName:"a"},"Arnold")),"."),(0,o.kt)("h2",{id:"docker"},"Docker"),(0,o.kt)("p",null,"First, make sure you have a recent version of Docker and\n",(0,o.kt)("a",{parentName:"p",href:"https://docs.docker.com/compose/install"},"Docker Compose")," installed on your\nlaptop:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-bash"},"$ docker -v\n Docker version 20.10.12, build e91ed57\n\n$ docker-compose --version\n docker-compose version 1.29.2, build 5becea4c\n")),(0,o.kt)("p",null,"\u26a0\ufe0f You may need to run the following commands with ",(0,o.kt)("inlineCode",{parentName:"p"},"sudo")," but this can be\navoided by assigning your user to the ",(0,o.kt)("inlineCode",{parentName:"p"},"docker")," group."),(0,o.kt)("h3",{id:"project-bootstrap"},"Project bootstrap"),(0,o.kt)("p",null,"The easiest way to start working on the project is to use our ",(0,o.kt)("inlineCode",{parentName:"p"},"Makefile"),":"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"$ make bootstrap\n")),(0,o.kt)("p",null,"This command builds the ",(0,o.kt)("inlineCode",{parentName:"p"},"app")," container, installs front-end and back-end\ndependencies, builds the front-end application and styles, and performs\ndatabase migrations. It's a good idea to use this command each time you are\npulling code from the project repository to avoid dependency-related or\nmigration-related issues."),(0,o.kt)("p",null,"Now that your ",(0,o.kt)("inlineCode",{parentName:"p"},"Docker")," services are ready to be used, start the full CMS by\nrunning:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"$ make run\n")),(0,o.kt)("h3",{id:"adding-content"},"Adding content"),(0,o.kt)("p",null,"Once the CMS is up and running, you can create a superuser account:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"$ make superuser\n")),(0,o.kt)("p",null,"You can create a basic demo site by running:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"$ make demo-site\n")),(0,o.kt)("p",null,"Note that if you don't create the demo site and start from a blank CMS, you\nwill get some errors requesting you to create some required root pages. So it\nis easier as a first approach to test the CMS with the demo site."),(0,o.kt)("p",null,"You should be able to view the site at ",(0,o.kt)("a",{parentName:"p",href:"http://localhost:8070"},"localhost:8070")),(0,o.kt)("h2",{id:"connecting-richie-to-an-lms"},"Connecting Richie to an LMS"),(0,o.kt)("p",null,"It is possible to use Richie as a catalogue aggregating courses from one or\nmore LMS without any specific connection. In this case, each course run in\nthe catalogue points to a course on the LMS, and the LMS points back to the\ncatalogue to browse courses."),(0,o.kt)("p",null,"This approach is used for example on ",(0,o.kt)("a",{parentName:"p",href:"https://www.fun-campus.fr"},"https://www.fun-campus.fr")," or\n",(0,o.kt)("a",{parentName:"p",href:"https://catalogue.edulib.org"},"https://catalogue.edulib.org"),"."),(0,o.kt)("p",null,"For a seamless user experience, it is possible to connect a Richie instance\nto an OpenEdX instance (or some other LMS like Moodle at the cost of minor\nadaptations), in several ways that we explain in the\n",(0,o.kt)("a",{parentName:"p",href:"lms-connection"},"LMS connection guide"),"."),(0,o.kt)("p",null,"This approach is used for example on ",(0,o.kt)("a",{parentName:"p",href:"https://www.fun-mooc.fr"},"https://www.fun-mooc.fr")," or\n",(0,o.kt)("a",{parentName:"p",href:"https://www.nau.edu.pt"},"https://www.nau.edu.pt"),"."))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/1681c897.04898a5f.js b/assets/js/1681c897.04898a5f.js deleted file mode 100644 index a537789571..0000000000 --- a/assets/js/1681c897.04898a5f.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[29228],{3905:(e,t,o)=>{o.d(t,{Zo:()=>s,kt:()=>h});var r=o(67294);function n(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function i(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,r)}return o}function a(e){for(var t=1;t=0||(n[o]=e[o]);return n}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,o)&&(n[o]=e[o])}return n}var l=r.createContext({}),u=function(e){var t=r.useContext(l),o=t;return e&&(o="function"==typeof e?e(t):a(a({},t),e)),o},s=function(e){var t=u(e.components);return r.createElement(l.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var o=e.components,n=e.mdxType,i=e.originalType,l=e.parentName,s=c(e,["components","mdxType","originalType","parentName"]),d=u(o),h=n,m=d["".concat(l,".").concat(h)]||d[h]||p[h]||i;return o?r.createElement(m,a(a({ref:t},s),{},{components:o})):r.createElement(m,a({ref:t},s))}));function h(e,t){var o=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var i=o.length,a=new Array(i);a[0]=d;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c.mdxType="string"==typeof e?e:n,a[1]=c;for(var u=2;u{o.r(t),o.d(t,{assets:()=>s,contentTitle:()=>l,default:()=>h,frontMatter:()=>c,metadata:()=>u,toc:()=>p});var r=o(83117),n=o(80102),i=(o(67294),o(3905)),a=["components"],c={id:"cookiecutter",title:"Start your own site",sidebar_label:"Start your own site"},l=void 0,u={unversionedId:"cookiecutter",id:"version-2.21.1/cookiecutter",title:"Start your own site",description:"We use Cookiecutter to help you",source:"@site/versioned_docs/version-2.21.1/cookiecutter.md",sourceDirName:".",slug:"/cookiecutter",permalink:"/docs/cookiecutter",draft:!1,tags:[],version:"2.21.1",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1680624606,formattedLastUpdatedAt:"Apr 4, 2023",frontMatter:{id:"cookiecutter",title:"Start your own site",sidebar_label:"Start your own site"},sidebar:"docs",previous:{title:"Discover Richie",permalink:"/docs/discover"},next:{title:"Search filters customization",permalink:"/docs/filters-customization"}},s={},p=[{value:"Run Cookiecutter",id:"run-cookiecutter",level:2},{value:"Bootstrap your project",id:"bootstrap-your-project",level:3},{value:"Theming",id:"theming",level:2},{value:"Update your Richie site factory",id:"update-your-richie-site-factory",level:2},{value:"Help us improve this project",id:"help-us-improve-this-project",level:2}],d={toc:p};function h(e){var t=e.components,o=(0,n.Z)(e,a);return(0,i.kt)("wrapper",(0,r.Z)({},d,o,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"We use ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/audreyr/cookiecutter"},"Cookiecutter")," to help you\nset up a production-ready learning portal website based on\n",(0,i.kt)("a",{parentName:"p",href:"https://github.com/openfun/richie"},"Richie")," in seconds."),(0,i.kt)("h2",{id:"run-cookiecutter"},"Run Cookiecutter"),(0,i.kt)("p",null,"There are 2 options to run Cookiecutter:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://cookiecutter.readthedocs.io/en/latest/installation.html"},"install it on your machine")),(0,i.kt)("li",{parentName:"ul"},"run it with Docker")),(0,i.kt)("p",null,"While you think of it, navigate to the directory in which you want to create\nyour site factory:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"cd /path/to/your/code/directory\n")),(0,i.kt)("p",null,"If you chose to install Cookiecutter, you can now run it against our\n",(0,i.kt)("a",{parentName:"p",href:"https://github.com/openfun/richie/tree/master/cookiecutter"},"template")," as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"cookiecutter gh:openfun/richie --directory cookiecutter --checkout v2.21.1\n")),(0,i.kt)("p",null,"If you didn't want to install it on your machine, we provide a Docker image\nbuilt with our ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/openfun/dockerfiles"},"own repository")," that you can use as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"docker run --rm -it -u $(id -u):$(id -g) -v $PWD:/app \\\nfundocker/cookiecutter gh:openfun/richie --directory cookiecutter --checkout v2.21.1\n")),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"--directory")," option is to indicate that our Cookiecutter template is in\na ",(0,i.kt)("inlineCode",{parentName:"p"},"cookiecutter")," directory inside our git repository and not at the root."),(0,i.kt)("p",null,'You will be prompted to enter an organization name, which will determine the\nname of your repository. For example, if you choose "foo" as organization\nname, your repository will be named ',(0,i.kt)("inlineCode",{parentName:"p"},"foo-richie-site-factory"),". It's\nnice if you keep it that way so all richie site factories follow this\nconvention."),(0,i.kt)("p",null,"When you confirm the organization name, Cookiecutter will generate your\nproject from the Cookiecutter template and place it at the level where you\ncurrently are."),(0,i.kt)("h3",{id:"bootstrap-your-project"},"Bootstrap your project"),(0,i.kt)("p",null,"Enter the newly created project and add a new site to your site factory:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"cd foo-richie-site-factory\nmake add-site\n")),(0,i.kt)("p",null,"This script also uses Cookiecutter against our ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/openfun/richie/tree/master/cookiecutter/%7B%7Bcookiecutter.organization%7D%7D-richie-site-factory/template"},"site template"),"."),(0,i.kt)("p",null,"Once your new site is created, activate it:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"bin/activate\n")),(0,i.kt)("p",null,"Now bootstrap the site to build its docker image, create its media folder,\ndatabase, etc.:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"make bootstrap\n")),(0,i.kt)("p",null,"Once the bootstrap phase is finished, you should be able to view the site at\n",(0,i.kt)("a",{parentName:"p",href:"http://localhost:8070"},"localhost:8070"),"."),(0,i.kt)("p",null,"You can create a full fledge demo to test your site by running:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"make demo-site\n")),(0,i.kt)("p",null,"Note that the README of your newly created site factory contains detailed\ninformation about how to configure and run a site."),(0,i.kt)("p",null,"Once you're happy with your site, don't forget to backup your work e.g. by\ncommitting it and pushing it to a new git repository."),(0,i.kt)("h2",{id:"theming"},"Theming"),(0,i.kt)("p",null,"You probably want to change the default theme. The cookiecutter adds an extra scss frontend folder with a couple of templates that you can use to change the default styling of the site."),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"sites//src/frontend/scss/extras/colors/_palette.scss")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"sites//src/frontend/scss/extras/colors/_theme.scss"))),(0,i.kt)("p",null,"To change the default logo of the site, you need to create the folder ",(0,i.kt)("inlineCode",{parentName:"p"},"sites//src/backend/base/static/richie/images")," and then override the new ",(0,i.kt)("inlineCode",{parentName:"p"},"logo.png")," picture."),(0,i.kt)("p",null,"For more advanced customization, refer to our recipes:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/docs/filters-customization"},"How to customize search filters")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/docs/frontend-overrides"},"How to override frontend components in Richie"))),(0,i.kt)("h2",{id:"update-your-richie-site-factory"},"Update your Richie site factory"),(0,i.kt)("p",null,"If we later improve our scripts, you will be able to update your own site\nfactory by replaying Cookiecutter. This will override your files in the\nproject's scaffolding but, don't worry, it will respect all the sites you\nwill have created in the ",(0,i.kt)("inlineCode",{parentName:"p"},"sites")," directory:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"cookiecutter --overwrite-if-exists gh:openfun/richie --directory=cookiecutter\n")),(0,i.kt)("h2",{id:"help-us-improve-this-project"},"Help us improve this project"),(0,i.kt)("p",null,"After starting your project, please submit an issue let us know how it went and\nwhat other features we should add to make it better."))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/1681c897.7941d6c3.js b/assets/js/1681c897.7941d6c3.js new file mode 100644 index 0000000000..f3e0d3c19f --- /dev/null +++ b/assets/js/1681c897.7941d6c3.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[29228],{3905:(e,t,o)=>{o.d(t,{Zo:()=>s,kt:()=>h});var r=o(67294);function n(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function i(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,r)}return o}function a(e){for(var t=1;t=0||(n[o]=e[o]);return n}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,o)&&(n[o]=e[o])}return n}var l=r.createContext({}),u=function(e){var t=r.useContext(l),o=t;return e&&(o="function"==typeof e?e(t):a(a({},t),e)),o},s=function(e){var t=u(e.components);return r.createElement(l.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var o=e.components,n=e.mdxType,i=e.originalType,l=e.parentName,s=c(e,["components","mdxType","originalType","parentName"]),d=u(o),h=n,m=d["".concat(l,".").concat(h)]||d[h]||p[h]||i;return o?r.createElement(m,a(a({ref:t},s),{},{components:o})):r.createElement(m,a({ref:t},s))}));function h(e,t){var o=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var i=o.length,a=new Array(i);a[0]=d;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c.mdxType="string"==typeof e?e:n,a[1]=c;for(var u=2;u{o.r(t),o.d(t,{assets:()=>s,contentTitle:()=>l,default:()=>h,frontMatter:()=>c,metadata:()=>u,toc:()=>p});var r=o(83117),n=o(80102),i=(o(67294),o(3905)),a=["components"],c={id:"cookiecutter",title:"Start your own site",sidebar_label:"Start your own site"},l=void 0,u={unversionedId:"cookiecutter",id:"version-2.21.1/cookiecutter",title:"Start your own site",description:"We use Cookiecutter to help you",source:"@site/versioned_docs/version-2.21.1/cookiecutter.md",sourceDirName:".",slug:"/cookiecutter",permalink:"/docs/2.21.1/cookiecutter",draft:!1,tags:[],version:"2.21.1",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1680624606,formattedLastUpdatedAt:"Apr 4, 2023",frontMatter:{id:"cookiecutter",title:"Start your own site",sidebar_label:"Start your own site"},sidebar:"docs",previous:{title:"Discover Richie",permalink:"/docs/2.21.1/discover"},next:{title:"Search filters customization",permalink:"/docs/2.21.1/filters-customization"}},s={},p=[{value:"Run Cookiecutter",id:"run-cookiecutter",level:2},{value:"Bootstrap your project",id:"bootstrap-your-project",level:3},{value:"Theming",id:"theming",level:2},{value:"Update your Richie site factory",id:"update-your-richie-site-factory",level:2},{value:"Help us improve this project",id:"help-us-improve-this-project",level:2}],d={toc:p};function h(e){var t=e.components,o=(0,n.Z)(e,a);return(0,i.kt)("wrapper",(0,r.Z)({},d,o,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"We use ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/audreyr/cookiecutter"},"Cookiecutter")," to help you\nset up a production-ready learning portal website based on\n",(0,i.kt)("a",{parentName:"p",href:"https://github.com/openfun/richie"},"Richie")," in seconds."),(0,i.kt)("h2",{id:"run-cookiecutter"},"Run Cookiecutter"),(0,i.kt)("p",null,"There are 2 options to run Cookiecutter:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"https://cookiecutter.readthedocs.io/en/latest/installation.html"},"install it on your machine")),(0,i.kt)("li",{parentName:"ul"},"run it with Docker")),(0,i.kt)("p",null,"While you think of it, navigate to the directory in which you want to create\nyour site factory:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"cd /path/to/your/code/directory\n")),(0,i.kt)("p",null,"If you chose to install Cookiecutter, you can now run it against our\n",(0,i.kt)("a",{parentName:"p",href:"https://github.com/openfun/richie/tree/master/cookiecutter"},"template")," as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"cookiecutter gh:openfun/richie --directory cookiecutter --checkout v2.21.1\n")),(0,i.kt)("p",null,"If you didn't want to install it on your machine, we provide a Docker image\nbuilt with our ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/openfun/dockerfiles"},"own repository")," that you can use as follows:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"docker run --rm -it -u $(id -u):$(id -g) -v $PWD:/app \\\nfundocker/cookiecutter gh:openfun/richie --directory cookiecutter --checkout v2.21.1\n")),(0,i.kt)("p",null,"The ",(0,i.kt)("inlineCode",{parentName:"p"},"--directory")," option is to indicate that our Cookiecutter template is in\na ",(0,i.kt)("inlineCode",{parentName:"p"},"cookiecutter")," directory inside our git repository and not at the root."),(0,i.kt)("p",null,'You will be prompted to enter an organization name, which will determine the\nname of your repository. For example, if you choose "foo" as organization\nname, your repository will be named ',(0,i.kt)("inlineCode",{parentName:"p"},"foo-richie-site-factory"),". It's\nnice if you keep it that way so all richie site factories follow this\nconvention."),(0,i.kt)("p",null,"When you confirm the organization name, Cookiecutter will generate your\nproject from the Cookiecutter template and place it at the level where you\ncurrently are."),(0,i.kt)("h3",{id:"bootstrap-your-project"},"Bootstrap your project"),(0,i.kt)("p",null,"Enter the newly created project and add a new site to your site factory:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"cd foo-richie-site-factory\nmake add-site\n")),(0,i.kt)("p",null,"This script also uses Cookiecutter against our ",(0,i.kt)("a",{parentName:"p",href:"https://github.com/openfun/richie/tree/master/cookiecutter/%7B%7Bcookiecutter.organization%7D%7D-richie-site-factory/template"},"site template"),"."),(0,i.kt)("p",null,"Once your new site is created, activate it:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"bin/activate\n")),(0,i.kt)("p",null,"Now bootstrap the site to build its docker image, create its media folder,\ndatabase, etc.:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"make bootstrap\n")),(0,i.kt)("p",null,"Once the bootstrap phase is finished, you should be able to view the site at\n",(0,i.kt)("a",{parentName:"p",href:"http://localhost:8070"},"localhost:8070"),"."),(0,i.kt)("p",null,"You can create a full fledge demo to test your site by running:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-bash"},"make demo-site\n")),(0,i.kt)("p",null,"Note that the README of your newly created site factory contains detailed\ninformation about how to configure and run a site."),(0,i.kt)("p",null,"Once you're happy with your site, don't forget to backup your work e.g. by\ncommitting it and pushing it to a new git repository."),(0,i.kt)("h2",{id:"theming"},"Theming"),(0,i.kt)("p",null,"You probably want to change the default theme. The cookiecutter adds an extra scss frontend folder with a couple of templates that you can use to change the default styling of the site."),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"sites//src/frontend/scss/extras/colors/_palette.scss")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("inlineCode",{parentName:"li"},"sites//src/frontend/scss/extras/colors/_theme.scss"))),(0,i.kt)("p",null,"To change the default logo of the site, you need to create the folder ",(0,i.kt)("inlineCode",{parentName:"p"},"sites//src/backend/base/static/richie/images")," and then override the new ",(0,i.kt)("inlineCode",{parentName:"p"},"logo.png")," picture."),(0,i.kt)("p",null,"For more advanced customization, refer to our recipes:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/docs/2.21.1/filters-customization"},"How to customize search filters")),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("a",{parentName:"li",href:"/docs/2.21.1/frontend-overrides"},"How to override frontend components in Richie"))),(0,i.kt)("h2",{id:"update-your-richie-site-factory"},"Update your Richie site factory"),(0,i.kt)("p",null,"If we later improve our scripts, you will be able to update your own site\nfactory by replaying Cookiecutter. This will override your files in the\nproject's scaffolding but, don't worry, it will respect all the sites you\nwill have created in the ",(0,i.kt)("inlineCode",{parentName:"p"},"sites")," directory:"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre"},"cookiecutter --overwrite-if-exists gh:openfun/richie --directory=cookiecutter\n")),(0,i.kt)("h2",{id:"help-us-improve-this-project"},"Help us improve this project"),(0,i.kt)("p",null,"After starting your project, please submit an issue let us know how it went and\nwhat other features we should add to make it better."))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/18b93cb3.436283f7.js b/assets/js/18b93cb3.436283f7.js new file mode 100644 index 0000000000..3707933e66 --- /dev/null +++ b/assets/js/18b93cb3.436283f7.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[3042],{29732:(e,t,l)=>{l.r(t),l.d(t,{default:()=>o});var n=l(39960),r=l(52263),a=l(7961),c=l(67294);const s=JSON.parse('["2.22.0","2.21.1","2.21.0","2.20.1","2.20.0","2.19.0","2.18.0","2.17.0","2.16.0","2.15.1","2.15.0","2.14.1","2.14.0","2.13.0","2.12.0","2.11.0","2.10.0","2.9.1","2.9.0","2.8.2","2.8.1","2.8.0","2.7.1","2.7.0","2.6.0","2.5.0","2.4.0","2.3.3","2.3.2","2.3.1","2.3.0","2.2.0","2.1.0","2.0.1","2.0.0","1.17","1.16","1.15","1.14","1.13","1.12"]');const o=function(){var e=(0,r.Z)().siteConfig,t=s[0];return c.createElement(a.Z,null,c.createElement("section",null,c.createElement("div",{className:"container"},c.createElement("div",{className:"post"},c.createElement("header",{className:"postHeader"},c.createElement("h1",null,e.title," versions and documentation")),c.createElement("p",null,"New versions of this project are shipped regularly. Every new version includes its own version of the documentation."),c.createElement("p",null,"Versions below ",c.createElement("code",null,"1.12.0")," did not have a dedicated documentation website."),c.createElement("h3",{id:"latest"},"Current version (Stable)"),c.createElement("table",{className:"versions"},c.createElement("tbody",null,c.createElement("tr",null,c.createElement("th",null,t),c.createElement("td",null,c.createElement(n.Z,{to:"docs/discover"},"Documentation")),c.createElement("td",null,c.createElement(n.Z,{to:e.customFields.repoUrl+"/releases/tag/v"+t},"Release Notes"))))),c.createElement("h3",{id:"rc"},"Pre-release versions"),c.createElement("table",{className:"versions"},c.createElement("tbody",null,c.createElement("tr",null,c.createElement("th",null,"master"),c.createElement("td",null,c.createElement(n.Z,{to:"docs/next/discover"},"Documentation")),c.createElement("td",null,c.createElement(n.Z,{to:e.customFields.repoUrl},"Source Code"))))),c.createElement("h3",{id:"archive"},"Past Versions"),c.createElement("p",null,"Here you can find previous versions of the documentation."),c.createElement("table",{className:"versions"},c.createElement("tbody",null,s.map((function(l){return l!==t&&c.createElement("tr",{key:l},c.createElement("th",null,l),c.createElement("td",null,c.createElement(n.Z,{to:"docs/"+l+"/discover"},"Documentation")),c.createElement("td",null,c.createElement(n.Z,{to:e.customFields.repoUrl+"/releases/tag/v"+(3===l.split(".").length?l:l+".0")},"Release Notes")))})))),c.createElement("p",null,"You can find past versions of this project on"," ",c.createElement(n.Z,{to:e.customFields.repoUrl},"GitHub"),".")))))}}}]); \ No newline at end of file diff --git a/assets/js/18b93cb3.fd17dff1.js b/assets/js/18b93cb3.fd17dff1.js deleted file mode 100644 index 84f91348ca..0000000000 --- a/assets/js/18b93cb3.fd17dff1.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[3042],{29732:(e,t,l)=>{l.r(t),l.d(t,{default:()=>o});var n=l(39960),r=l(52263),a=l(7961),c=l(67294);const s=JSON.parse('["2.21.1","2.21.0","2.20.1","2.20.0","2.19.0","2.18.0","2.17.0","2.16.0","2.15.1","2.15.0","2.14.1","2.14.0","2.13.0","2.12.0","2.11.0","2.10.0","2.9.1","2.9.0","2.8.2","2.8.1","2.8.0","2.7.1","2.7.0","2.6.0","2.5.0","2.4.0","2.3.3","2.3.2","2.3.1","2.3.0","2.2.0","2.1.0","2.0.1","2.0.0","1.17","1.16","1.15","1.14","1.13","1.12"]');const o=function(){var e=(0,r.Z)().siteConfig,t=s[0];return c.createElement(a.Z,null,c.createElement("section",null,c.createElement("div",{className:"container"},c.createElement("div",{className:"post"},c.createElement("header",{className:"postHeader"},c.createElement("h1",null,e.title," versions and documentation")),c.createElement("p",null,"New versions of this project are shipped regularly. Every new version includes its own version of the documentation."),c.createElement("p",null,"Versions below ",c.createElement("code",null,"1.12.0")," did not have a dedicated documentation website."),c.createElement("h3",{id:"latest"},"Current version (Stable)"),c.createElement("table",{className:"versions"},c.createElement("tbody",null,c.createElement("tr",null,c.createElement("th",null,t),c.createElement("td",null,c.createElement(n.Z,{to:"docs/discover"},"Documentation")),c.createElement("td",null,c.createElement(n.Z,{to:e.customFields.repoUrl+"/releases/tag/v"+t},"Release Notes"))))),c.createElement("h3",{id:"rc"},"Pre-release versions"),c.createElement("table",{className:"versions"},c.createElement("tbody",null,c.createElement("tr",null,c.createElement("th",null,"master"),c.createElement("td",null,c.createElement(n.Z,{to:"docs/next/discover"},"Documentation")),c.createElement("td",null,c.createElement(n.Z,{to:e.customFields.repoUrl},"Source Code"))))),c.createElement("h3",{id:"archive"},"Past Versions"),c.createElement("p",null,"Here you can find previous versions of the documentation."),c.createElement("table",{className:"versions"},c.createElement("tbody",null,s.map((function(l){return l!==t&&c.createElement("tr",{key:l},c.createElement("th",null,l),c.createElement("td",null,c.createElement(n.Z,{to:"docs/"+l+"/discover"},"Documentation")),c.createElement("td",null,c.createElement(n.Z,{to:e.customFields.repoUrl+"/releases/tag/v"+(3===l.split(".").length?l:l+".0")},"Release Notes")))})))),c.createElement("p",null,"You can find past versions of this project on"," ",c.createElement(n.Z,{to:e.customFields.repoUrl},"GitHub"),".")))))}}}]); \ No newline at end of file diff --git a/assets/js/26e576cc.6a8ab89b.js b/assets/js/26e576cc.6a8ab89b.js deleted file mode 100644 index 82b8a497d3..0000000000 --- a/assets/js/26e576cc.6a8ab89b.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[40043],{3905:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>h});var a=n(67294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var s=a.createContext({}),p=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},c=function(e){var t=p(e.components);return a.createElement(s.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},u=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,s=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),u=p(n),h=r,m=u["".concat(s,".").concat(h)]||u[h]||d[h]||o;return n?a.createElement(m,i(i({ref:t},c),{},{components:n})):a.createElement(m,i({ref:t},c))}));function h(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,i=new Array(o);i[0]=u;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:r,i[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>h,frontMatter:()=>l,metadata:()=>p,toc:()=>d});var a=n(83117),r=n(80102),o=(n(67294),n(3905)),i=["components"],l={id:"django-react-interop",title:"Connecting React components with Django",sidebar_label:"Django & React"},s=void 0,p={unversionedId:"django-react-interop",id:"version-2.21.1/django-react-interop",title:"Connecting React components with Django",description:"richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.",source:"@site/versioned_docs/version-2.21.1/django-react-interop.md",sourceDirName:".",slug:"/django-react-interop",permalink:"/docs/django-react-interop",draft:!1,tags:[],version:"2.21.1",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1680624606,formattedLastUpdatedAt:"Apr 4, 2023",frontMatter:{id:"django-react-interop",title:"Connecting React components with Django",sidebar_label:"Django & React"},sidebar:"docs",previous:{title:"Search filters customization",permalink:"/docs/filters-customization"},next:{title:"Building the frontend",permalink:"/docs/building-the-frontend"}},c={},d=[{value:"Rendering components",id:"rendering-components",level:2},{value:"Example",id:"example",level:3},{value:"Passing properties to components",id:"passing-properties-to-components",level:2},{value:"Example",id:"example-1",level:3},{value:"Built-in components",id:"built-in-components",level:2},{value:"<RootSearchSuggestField />",id:"rootsearchsuggestfield-",level:3},{value:"<Search />",id:"search-",level:3},{value:"<SearchSuggestField />",id:"searchsuggestfield-",level:3},{value:"<UserLogin />",id:"userlogin-",level:3},{value:"Context",id:"context",level:2}],u={toc:d};function h(e){var t=e.components,n=(0,r.Z)(e,i);return(0,o.kt)("wrapper",(0,a.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,(0,o.kt)("inlineCode",{parentName:"p"},"richie")," is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages."),(0,o.kt)("h2",{id:"rendering-components"},"Rendering components"),(0,o.kt)("p",null,"We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there."),(0,o.kt)("p",null,"We decided to use a specific CSS class name along with its modifiers. We reserve the ",(0,o.kt)("inlineCode",{parentName:"p"},"richie-react")," class and its modified children for this purpose."),(0,o.kt)("p",null,"Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the ",(0,o.kt)("inlineCode",{parentName:"p"},"lang")," attribute of the ",(0,o.kt)("inlineCode",{parentName:"p"},"")," element, which is a requirement to have an accessible page anyway."),(0,o.kt)("p",null,"They use the BCP47/RFC5646 format."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'\n')),(0,o.kt)("h3",{id:"example"},"Example"),(0,o.kt)("p",null,"Here is how we would call a ",(0,o.kt)("inlineCode",{parentName:"p"},"")," component from a template, a plugin or a snippet:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'\n')),(0,o.kt)("p",null,"When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element."),(0,o.kt)("h2",{id:"passing-properties-to-components"},"Passing properties to components"),(0,o.kt)("p",null,'Some of Richie\'s React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.'),(0,o.kt)("p",null,"Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM."),(0,o.kt)("p",null,"We can add a ",(0,o.kt)("inlineCode",{parentName:"p"},"data-props")," attribute on the element with the ",(0,o.kt)("inlineCode",{parentName:"p"},"richie-react")," class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a ",(0,o.kt)("inlineCode",{parentName:"p"},"propName={propValue}")," to the React component."),(0,o.kt)("h3",{id:"example-1"},"Example"),(0,o.kt)("p",null,"Here is how we would pass a ",(0,o.kt)("inlineCode",{parentName:"p"},'categories={[ "sociology", "anthropology" ]}')," prop to our ",(0,o.kt)("inlineCode",{parentName:"p"},"")," component:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'\n')),(0,o.kt)("p",null,"When the component is rendered, it will be passed a ",(0,o.kt)("inlineCode",{parentName:"p"},"categories")," prop with the relevant categories."),(0,o.kt)("h2",{id:"built-in-components"},"Built-in components"),(0,o.kt)("p",null,"Here are the React component that Richie comes with and uses out of the box."),(0,o.kt)("h3",{id:"rootsearchsuggestfield-"},"<","RootSearchSuggestField /",">"),(0,o.kt)("p",null,"Renders a course search bar, like the one that appears in the default ",(0,o.kt)("inlineCode",{parentName:"p"},"Search")," page."),(0,o.kt)("p",null,"Interactions will send the user to the ",(0,o.kt)("inlineCode",{parentName:"p"},"courseSearchPageUrl")," url passed in the props, including the selected filter and/or search terms."),(0,o.kt)("p",null,"It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results."),(0,o.kt)("p",null,"Props:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"courseSearchPageUrl")," ","[required]"," \u2014 URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"context")," ","[required]"," \u2014 see ",(0,o.kt)("a",{parentName:"li",href:"#context"},"context"),".")),(0,o.kt)("h3",{id:"search-"},"<","Search /",">"),(0,o.kt)("p",null,"Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with ",(0,o.kt)("inlineCode",{parentName:"p"},""),") nor the page title."),(0,o.kt)("p",null,"NB: the ",(0,o.kt)("inlineCode",{parentName:"p"},"Search")," Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake."),(0,o.kt)("p",null,"Props:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"context")," ","[required]"," \u2014 see ",(0,o.kt)("a",{parentName:"li",href:"#context"},"context"),".")),(0,o.kt)("h3",{id:"searchsuggestfield-"},"<","SearchSuggestField /",">"),(0,o.kt)("p",null,"Renders the course search bar that interacts directly with ",(0,o.kt)("inlineCode",{parentName:"p"},""),"."),(0,o.kt)("p",null,"It automatically communicates with ",(0,o.kt)("inlineCode",{parentName:"p"},"")," through browser history APIs and a shared React provider. This one, unlike ",(0,o.kt)("inlineCode",{parentName:"p"},""),", is meant to be used in combination with ",(0,o.kt)("inlineCode",{parentName:"p"},"")," (on the same page)."),(0,o.kt)("p",null,"Props:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"context")," ","[required]"," \u2014 see ",(0,o.kt)("a",{parentName:"li",href:"#context"},"context"),".")),(0,o.kt)("h3",{id:"userlogin-"},"<","UserLogin /",">"),(0,o.kt)("p",null,"Renders a component that uses the ",(0,o.kt)("inlineCode",{parentName:"p"},"/users/whoami")," endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button."),(0,o.kt)("p",null,"Props:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"loginUrl")," ","[required]",' \u2014 the URL where the user is sent when they click on "Log in";'),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"logoutUrl")," ","[required]"," \u2014 a link that logs the user out and redirects them (can be the standard django logout URL);"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"signupUrl")," ","[required]",' \u2014 the URL where the user is sent when they click on "Sign up".')),(0,o.kt)("h2",{id:"context"},"Context"),(0,o.kt)("p",null,"All built-in components for Richie accept a ",(0,o.kt)("inlineCode",{parentName:"p"},"context")," prop, that may be required or optional, depending on the component."),(0,o.kt)("p",null,"It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie."),(0,o.kt)("p",null,"Here is the expected shape for this object:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'{\n assets: {\n // SVG sprite used throughout Richie\n icons: "/path/to/icons/sprite.svg"\n }\n}\n')),(0,o.kt)("p",null,"Note that it might be expanded in further versions of Richie."))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/26e576cc.f7bdcbb6.js b/assets/js/26e576cc.f7bdcbb6.js new file mode 100644 index 0000000000..eb9a6b28d3 --- /dev/null +++ b/assets/js/26e576cc.f7bdcbb6.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[40043],{3905:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>h});var a=n(67294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var s=a.createContext({}),p=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},c=function(e){var t=p(e.components);return a.createElement(s.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},u=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,s=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),u=p(n),h=r,m=u["".concat(s,".").concat(h)]||u[h]||d[h]||o;return n?a.createElement(m,i(i({ref:t},c),{},{components:n})):a.createElement(m,i({ref:t},c))}));function h(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,i=new Array(o);i[0]=u;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:r,i[1]=l;for(var p=2;p{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>h,frontMatter:()=>l,metadata:()=>p,toc:()=>d});var a=n(83117),r=n(80102),o=(n(67294),n(3905)),i=["components"],l={id:"django-react-interop",title:"Connecting React components with Django",sidebar_label:"Django & React"},s=void 0,p={unversionedId:"django-react-interop",id:"version-2.21.1/django-react-interop",title:"Connecting React components with Django",description:"richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.",source:"@site/versioned_docs/version-2.21.1/django-react-interop.md",sourceDirName:".",slug:"/django-react-interop",permalink:"/docs/2.21.1/django-react-interop",draft:!1,tags:[],version:"2.21.1",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1680624606,formattedLastUpdatedAt:"Apr 4, 2023",frontMatter:{id:"django-react-interop",title:"Connecting React components with Django",sidebar_label:"Django & React"},sidebar:"docs",previous:{title:"Search filters customization",permalink:"/docs/2.21.1/filters-customization"},next:{title:"Building the frontend",permalink:"/docs/2.21.1/building-the-frontend"}},c={},d=[{value:"Rendering components",id:"rendering-components",level:2},{value:"Example",id:"example",level:3},{value:"Passing properties to components",id:"passing-properties-to-components",level:2},{value:"Example",id:"example-1",level:3},{value:"Built-in components",id:"built-in-components",level:2},{value:"<RootSearchSuggestField />",id:"rootsearchsuggestfield-",level:3},{value:"<Search />",id:"search-",level:3},{value:"<SearchSuggestField />",id:"searchsuggestfield-",level:3},{value:"<UserLogin />",id:"userlogin-",level:3},{value:"Context",id:"context",level:2}],u={toc:d};function h(e){var t=e.components,n=(0,r.Z)(e,i);return(0,o.kt)("wrapper",(0,a.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,(0,o.kt)("inlineCode",{parentName:"p"},"richie")," is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages."),(0,o.kt)("h2",{id:"rendering-components"},"Rendering components"),(0,o.kt)("p",null,"We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there."),(0,o.kt)("p",null,"We decided to use a specific CSS class name along with its modifiers. We reserve the ",(0,o.kt)("inlineCode",{parentName:"p"},"richie-react")," class and its modified children for this purpose."),(0,o.kt)("p",null,"Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the ",(0,o.kt)("inlineCode",{parentName:"p"},"lang")," attribute of the ",(0,o.kt)("inlineCode",{parentName:"p"},"")," element, which is a requirement to have an accessible page anyway."),(0,o.kt)("p",null,"They use the BCP47/RFC5646 format."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'\n')),(0,o.kt)("h3",{id:"example"},"Example"),(0,o.kt)("p",null,"Here is how we would call a ",(0,o.kt)("inlineCode",{parentName:"p"},"")," component from a template, a plugin or a snippet:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'\n')),(0,o.kt)("p",null,"When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element."),(0,o.kt)("h2",{id:"passing-properties-to-components"},"Passing properties to components"),(0,o.kt)("p",null,'Some of Richie\'s React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.'),(0,o.kt)("p",null,"Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM."),(0,o.kt)("p",null,"We can add a ",(0,o.kt)("inlineCode",{parentName:"p"},"data-props")," attribute on the element with the ",(0,o.kt)("inlineCode",{parentName:"p"},"richie-react")," class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a ",(0,o.kt)("inlineCode",{parentName:"p"},"propName={propValue}")," to the React component."),(0,o.kt)("h3",{id:"example-1"},"Example"),(0,o.kt)("p",null,"Here is how we would pass a ",(0,o.kt)("inlineCode",{parentName:"p"},'categories={[ "sociology", "anthropology" ]}')," prop to our ",(0,o.kt)("inlineCode",{parentName:"p"},"")," component:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'\n')),(0,o.kt)("p",null,"When the component is rendered, it will be passed a ",(0,o.kt)("inlineCode",{parentName:"p"},"categories")," prop with the relevant categories."),(0,o.kt)("h2",{id:"built-in-components"},"Built-in components"),(0,o.kt)("p",null,"Here are the React component that Richie comes with and uses out of the box."),(0,o.kt)("h3",{id:"rootsearchsuggestfield-"},"<","RootSearchSuggestField /",">"),(0,o.kt)("p",null,"Renders a course search bar, like the one that appears in the default ",(0,o.kt)("inlineCode",{parentName:"p"},"Search")," page."),(0,o.kt)("p",null,"Interactions will send the user to the ",(0,o.kt)("inlineCode",{parentName:"p"},"courseSearchPageUrl")," url passed in the props, including the selected filter and/or search terms."),(0,o.kt)("p",null,"It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results."),(0,o.kt)("p",null,"Props:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"courseSearchPageUrl")," ","[required]"," \u2014 URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"context")," ","[required]"," \u2014 see ",(0,o.kt)("a",{parentName:"li",href:"#context"},"context"),".")),(0,o.kt)("h3",{id:"search-"},"<","Search /",">"),(0,o.kt)("p",null,"Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with ",(0,o.kt)("inlineCode",{parentName:"p"},""),") nor the page title."),(0,o.kt)("p",null,"NB: the ",(0,o.kt)("inlineCode",{parentName:"p"},"Search")," Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake."),(0,o.kt)("p",null,"Props:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"context")," ","[required]"," \u2014 see ",(0,o.kt)("a",{parentName:"li",href:"#context"},"context"),".")),(0,o.kt)("h3",{id:"searchsuggestfield-"},"<","SearchSuggestField /",">"),(0,o.kt)("p",null,"Renders the course search bar that interacts directly with ",(0,o.kt)("inlineCode",{parentName:"p"},""),"."),(0,o.kt)("p",null,"It automatically communicates with ",(0,o.kt)("inlineCode",{parentName:"p"},"")," through browser history APIs and a shared React provider. This one, unlike ",(0,o.kt)("inlineCode",{parentName:"p"},""),", is meant to be used in combination with ",(0,o.kt)("inlineCode",{parentName:"p"},"")," (on the same page)."),(0,o.kt)("p",null,"Props:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"context")," ","[required]"," \u2014 see ",(0,o.kt)("a",{parentName:"li",href:"#context"},"context"),".")),(0,o.kt)("h3",{id:"userlogin-"},"<","UserLogin /",">"),(0,o.kt)("p",null,"Renders a component that uses the ",(0,o.kt)("inlineCode",{parentName:"p"},"/users/whoami")," endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button."),(0,o.kt)("p",null,"Props:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"loginUrl")," ","[required]",' \u2014 the URL where the user is sent when they click on "Log in";'),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"logoutUrl")," ","[required]"," \u2014 a link that logs the user out and redirects them (can be the standard django logout URL);"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("inlineCode",{parentName:"li"},"signupUrl")," ","[required]",' \u2014 the URL where the user is sent when they click on "Sign up".')),(0,o.kt)("h2",{id:"context"},"Context"),(0,o.kt)("p",null,"All built-in components for Richie accept a ",(0,o.kt)("inlineCode",{parentName:"p"},"context")," prop, that may be required or optional, depending on the component."),(0,o.kt)("p",null,"It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie."),(0,o.kt)("p",null,"Here is the expected shape for this object:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},'{\n assets: {\n // SVG sprite used throughout Richie\n icons: "/path/to/icons/sprite.svg"\n }\n}\n')),(0,o.kt)("p",null,"Note that it might be expanded in further versions of Richie."))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/278477af.3c3b9039.js b/assets/js/278477af.3c3b9039.js new file mode 100644 index 0000000000..e2657660df --- /dev/null +++ b/assets/js/278477af.3c3b9039.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[85756],{3905:(e,n,t)=>{t.d(n,{Zo:()=>p,kt:()=>m});var o=t(67294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);n&&(o=o.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,o)}return t}function a(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var s=o.createContext({}),c=function(e){var n=o.useContext(s),t=n;return e&&(t="function"==typeof e?e(n):a(a({},n),e)),t},p=function(e){var n=c(e.components);return o.createElement(s.Provider,{value:n},e.children)},u={inlineCode:"code",wrapper:function(e){var n=e.children;return o.createElement(o.Fragment,{},n)}},d=o.forwardRef((function(e,n){var t=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),d=c(t),m=i,h=d["".concat(s,".").concat(m)]||d[m]||u[m]||r;return t?o.createElement(h,a(a({ref:n},p),{},{components:t})):o.createElement(h,a({ref:n},p))}));function m(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var r=t.length,a=new Array(r);a[0]=d;var l={};for(var s in n)hasOwnProperty.call(n,s)&&(l[s]=n[s]);l.originalType=e,l.mdxType="string"==typeof e?e:i,a[1]=l;for(var c=2;c{t.r(n),t.d(n,{assets:()=>p,contentTitle:()=>s,default:()=>m,frontMatter:()=>l,metadata:()=>c,toc:()=>u});var o=t(83117),i=t(80102),r=(t(67294),t(3905)),a=["components"],l={id:"lms-connection",title:"Connecting Richie with one or more LMS",sidebar_label:"LMS connection"},s=void 0,c={unversionedId:"lms-connection",id:"version-2.22.0/lms-connection",title:"Connecting Richie with one or more LMS",description:"Connecting Richie to an LMS",source:"@site/versioned_docs/version-2.22.0/lms-connection.md",sourceDirName:".",slug:"/lms-connection",permalink:"/docs/lms-connection",draft:!1,tags:[],version:"2.22.0",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1682069684,formattedLastUpdatedAt:"Apr 21, 2023",frontMatter:{id:"lms-connection",title:"Connecting Richie with one or more LMS",sidebar_label:"LMS connection"},sidebar:"docs",previous:{title:"I18n",permalink:"/docs/internationalization"},next:{title:"Web Analytics",permalink:"/docs/web-analytics"}},p={},u=[{value:"Connecting Richie to an LMS",id:"connecting-richie-to-an-lms",level:2},{value:"1. Displaying connection status",id:"1-displaying-connection-status",level:3},{value:"2. Seamless enrollment",id:"2-seamless-enrollment",level:3},{value:"3. Synchronizing course runs details",id:"3-synchronizing-course-runs-details",level:3},{value:"4. Joanie, the enrollment manager",id:"4-joanie-the-enrollment-manager",level:3},{value:"Development",id:"development",level:2}],d={toc:u};function m(e){var n=e.components,t=(0,i.Z)(e,a);return(0,r.kt)("wrapper",(0,o.Z)({},d,t,{components:n,mdxType:"MDXLayout"}),(0,r.kt)("h2",{id:"connecting-richie-to-an-lms"},"Connecting Richie to an LMS"),(0,r.kt)("p",null,"Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated\nseamless experience."),(0,r.kt)("p",null,"As of today, each approach has been implemented for OpenEdX but the same could be done for\nother LMSes like Moodle, at the cost of minor adaptations."),(0,r.kt)("h3",{id:"1-displaying-connection-status"},"1. Displaying connection status"),(0,r.kt)("p",null,"OpenEdX can be configured to allow CORS requests. Doing so allows Richie to retrieve a user's\nconnection status from OpenEdx and display the user's profile information directly on the Richie\nsite: username, dashboard url, etc."),(0,r.kt)("p",null,"In this approach, a user visiting your Richie site and trying to signup or login, is sent to the\nOpenEdX site for authentication and is redirected back to the Richie site upon successful login."),(0,r.kt)("p",null,"You can see this in action on ",(0,r.kt)("a",{parentName:"p",href:"https://www.fun-mooc.fr"},"https://www.fun-mooc.fr"),"."),(0,r.kt)("p",null,"We provide detailed instructions on\n",(0,r.kt)("a",{parentName:"p",href:"/docs/displaying-connection-status"},"how to configure displaying OpenEdX connection status in Richie"),"."),(0,r.kt)("h3",{id:"2-seamless-enrollment"},"2. Seamless enrollment"),(0,r.kt)("p",null,"Thanks to OpenEdX's enrollment API, it is possible to let users enroll on course runs without\nleaving Richie."),(0,r.kt)("p",null,"You can see this in action on ",(0,r.kt)("a",{parentName:"p",href:"https://www.fun-mooc.fr"},"https://www.fun-mooc.fr"),"."),(0,r.kt)("blockquote",null,(0,r.kt)("p",{parentName:"blockquote"},"This feature requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that\nare both subdomains of the same root domain, e.g. ",(0,r.kt)("inlineCode",{parentName:"p"},"richie.example.com")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"lms.example.com"),".")),(0,r.kt)("p",null,"You should read our guide on ",(0,r.kt)("a",{parentName:"p",href:"lms-backends"},"how to use OpenEdX as LMS backend for Richie"),"."),(0,r.kt)("h3",{id:"3-synchronizing-course-runs-details"},"3. Synchronizing course runs details"),(0,r.kt)("p",null,"Course runs in Richie can be handled manually, filling all fields via the DjangoCMS front-end\nediting interface. But a better way to handle course runs is to synchronize them automatically\nfrom your LMS using the course run synchronization API."),(0,r.kt)("p",null,"Please refer to our guide on ",(0,r.kt)("a",{parentName:"p",href:"synchronizing-course-runs"},"how to synchronize course runs between Richie and OpenEdx")),(0,r.kt)("h3",{id:"4-joanie-the-enrollment-manager"},"4. Joanie, the enrollment manager"),(0,r.kt)("p",null,"For more advanced use cases, we have started a new project called ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/openfun/joanie"},"Joanie")," which acts as an\nenrollment manager for Richie."),(0,r.kt)("p",null,"Authentication in Joanie is done via JWT Tokens for maximum flexibility and decoupling in\nidentity management."),(0,r.kt)("p",null,"The project started early 2021, but over time, Joanie will handle:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"paid enrollments / certification"),(0,r.kt)("li",{parentName:"ul"},"micro-credentials"),(0,r.kt)("li",{parentName:"ul"},"user dashboard"),(0,r.kt)("li",{parentName:"ul"},"cohorts management (academic or B2B)"),(0,r.kt)("li",{parentName:"ul"},"multi-LMS catalogs"),(0,r.kt)("li",{parentName:"ul"},"time based enrollment")),(0,r.kt)("h2",{id:"development"},"Development"),(0,r.kt)("p",null,"For development purposes, the docker-compose project provided on\n",(0,r.kt)("a",{parentName:"p",href:"https://github.com/openfun/richie"},"Richie's code repository")," is pre-configured to connect\nwith an OpenEdx instance started with\n",(0,r.kt)("a",{parentName:"p",href:"https://github.com/openfun/openedx-docker"},"OpenEdx Docker"),", which provides a ready-to-use\ndocker-compose stack of OpenEdx in several flavors. Head over to\n",(0,r.kt)("a",{parentName:"p",href:"https://github.com/openfun/openedx-docker#readme"},"OpenEdx Docker README")," for instructions on how to bootstrap an OpenEdX instance."),(0,r.kt)("p",null,"Now, start both the OpenEdX and Richie projects separately with ",(0,r.kt)("inlineCode",{parentName:"p"},"make run"),"."),(0,r.kt)("p",null,"Richie should respond on ",(0,r.kt)("inlineCode",{parentName:"p"},"http://localhost:8070"),", OpenEdx on ",(0,r.kt)("inlineCode",{parentName:"p"},"http://localhost:8073")," and both\napps should be able to communicate with each other via the network bridge defined in\ndocker-compose."),(0,r.kt)("p",null,"If you want to activate ",(0,r.kt)("a",{parentName:"p",href:"#2-seamless-enrollment"},"seamless enrollment")," locally for development,\nyou will need to set up TLS domains for both Richie and OpenEdX. To do this, head over to our\nguide on ",(0,r.kt)("a",{parentName:"p",href:"tls-connection"},"setting-up TLS connections for Richie and OpenEdX"),"."))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/2bd76b34.8eea0d83.js b/assets/js/2bd76b34.8eea0d83.js deleted file mode 100644 index 2974944ba8..0000000000 --- a/assets/js/2bd76b34.8eea0d83.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[68572],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>h});var r=n(67294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var c=r.createContext({}),s=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},u=function(e){var t=s(e.components);return r.createElement(c.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},p=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,c=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),p=s(n),h=a,f=p["".concat(c,".").concat(h)]||p[h]||d[h]||o;return n?r.createElement(f,i(i({ref:t},u),{},{components:n})):r.createElement(f,i({ref:t},u))}));function h(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=p;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l.mdxType="string"==typeof e?e:a,i[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>u,contentTitle:()=>c,default:()=>h,frontMatter:()=>l,metadata:()=>s,toc:()=>d});var r=n(83117),a=n(80102),o=(n(67294),n(3905)),i=["components"],l={id:"internationalization",title:"Internationalization",sidebar_label:"I18n"},c=void 0,s={unversionedId:"internationalization",id:"version-2.21.1/internationalization",title:"Internationalization",description:"richie has built-in localization and internationalization:",source:"@site/versioned_docs/version-2.21.1/internationalization.md",sourceDirName:".",slug:"/internationalization",permalink:"/docs/internationalization",draft:!1,tags:[],version:"2.21.1",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1680624606,formattedLastUpdatedAt:"Apr 4, 2023",frontMatter:{id:"internationalization",title:"Internationalization",sidebar_label:"I18n"},sidebar:"docs",previous:{title:"Frontend overrides",permalink:"/docs/frontend-overrides"},next:{title:"LMS connection",permalink:"/docs/lms-connection"}},u={},d=[{value:"Contributing as a translator or proof-reader",id:"contributing-as-a-translator-or-proof-reader",level:2},{value:"Sign-up on Crowdin",id:"sign-up-on-crowdin",level:3},{value:"Join the Richie project",id:"join-the-richie-project",level:3},{value:"Add a new language",id:"add-a-new-language",level:3}],p={toc:d};function h(e){var t=e.components,l=(0,a.Z)(e,i);return(0,o.kt)("wrapper",(0,r.Z)({},p,l,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,(0,o.kt)("inlineCode",{parentName:"p"},"richie")," has built-in localization and internationalization:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,"),(0,o.kt)("li",{parentName:"ul"},"On the frontend, we use React Intl.")),(0,o.kt)("h2",{id:"contributing-as-a-translator-or-proof-reader"},"Contributing as a translator or proof-reader"),(0,o.kt)("p",null,"We use the ",(0,o.kt)("a",{parentName:"p",href:"https://crowdin.com"},"Crowdin")," web platform to translate Richie to different languages.\nAll translations are hosted at ",(0,o.kt)("a",{parentName:"p",href:"https://i18n.richie.education"},"https://i18n.richie.education"),", which allows translators and\nproof-readers to contribute on translations in the languages they master."),(0,o.kt)("h3",{id:"sign-up-on-crowdin"},"Sign-up on Crowdin"),(0,o.kt)("p",null,"If you don't have an account on Crowdin already, go to ",(0,o.kt)("a",{parentName:"p",href:"https://accounts.crowdin.com/register"},"https://accounts.crowdin.com/register")," and\nfill out the form to create a free account."),(0,o.kt)("h3",{id:"join-the-richie-project"},"Join the Richie project"),(0,o.kt)("p",null,"Now that you have an account on Crowdin,\n",(0,o.kt)("a",{parentName:"p",href:"https://crowdin.com/project/richie"},'look for the project called "Richie"'),', select the language\non which you wish to contribute and click the "Join" button as demonstrated below:'),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"How to join Richie on Crowdin",src:n(50792).Z,width:"2462",height:"1506"})),(0,o.kt)("p",null,"We will then review you application and you should soon start translating strings!"),(0,o.kt)("p",null,"For more information on how Crowdin works, you can refer to\n",(0,o.kt)("a",{parentName:"p",href:"https://support.crowdin.com"},"their documentation"),"."),(0,o.kt)("h3",{id:"add-a-new-language"},"Add a new language"),(0,o.kt)("p",null,'If Richie is not yet translated in the language you want, let us know by clicking the "contact"\nlink on ',(0,o.kt)("a",{parentName:"p",href:"https://i18n.richie.education"},"Richie's Crowdin profile page")," and we will consider adding\nit."),(0,o.kt)("p",null,"If you request a new language, the Richie community will expect you to keep this language\nup-to-date each time strings are modified or new strings are added, and this before each\nrelease."),(0,o.kt)("p",null,"Before asking for a new language, make sure it does not already exist. If your language already\nexists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider\ncontributing on the existing language if your resources to contribute are limited."))}h.isMDXComponent=!0},50792:(e,t,n)=>{n.d(t,{Z:()=>r});const r=n.p+"assets/images/crowdin-join-richie-fd505b1a132bafb2bdafc715649a7c0f.gif"}}]); \ No newline at end of file diff --git a/assets/js/2bd76b34.aa933374.js b/assets/js/2bd76b34.aa933374.js new file mode 100644 index 0000000000..61bb578af0 --- /dev/null +++ b/assets/js/2bd76b34.aa933374.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[68572],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>h});var r=n(67294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var c=r.createContext({}),s=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},u=function(e){var t=s(e.components);return r.createElement(c.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},p=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,c=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),p=s(n),h=a,f=p["".concat(c,".").concat(h)]||p[h]||d[h]||o;return n?r.createElement(f,i(i({ref:t},u),{},{components:n})):r.createElement(f,i({ref:t},u))}));function h(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=p;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l.mdxType="string"==typeof e?e:a,i[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>u,contentTitle:()=>c,default:()=>h,frontMatter:()=>l,metadata:()=>s,toc:()=>d});var r=n(83117),a=n(80102),o=(n(67294),n(3905)),i=["components"],l={id:"internationalization",title:"Internationalization",sidebar_label:"I18n"},c=void 0,s={unversionedId:"internationalization",id:"version-2.21.1/internationalization",title:"Internationalization",description:"richie has built-in localization and internationalization:",source:"@site/versioned_docs/version-2.21.1/internationalization.md",sourceDirName:".",slug:"/internationalization",permalink:"/docs/2.21.1/internationalization",draft:!1,tags:[],version:"2.21.1",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1680624606,formattedLastUpdatedAt:"Apr 4, 2023",frontMatter:{id:"internationalization",title:"Internationalization",sidebar_label:"I18n"},sidebar:"docs",previous:{title:"Frontend overrides",permalink:"/docs/2.21.1/frontend-overrides"},next:{title:"LMS connection",permalink:"/docs/2.21.1/lms-connection"}},u={},d=[{value:"Contributing as a translator or proof-reader",id:"contributing-as-a-translator-or-proof-reader",level:2},{value:"Sign-up on Crowdin",id:"sign-up-on-crowdin",level:3},{value:"Join the Richie project",id:"join-the-richie-project",level:3},{value:"Add a new language",id:"add-a-new-language",level:3}],p={toc:d};function h(e){var t=e.components,l=(0,a.Z)(e,i);return(0,o.kt)("wrapper",(0,r.Z)({},p,l,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,(0,o.kt)("inlineCode",{parentName:"p"},"richie")," has built-in localization and internationalization:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,"),(0,o.kt)("li",{parentName:"ul"},"On the frontend, we use React Intl.")),(0,o.kt)("h2",{id:"contributing-as-a-translator-or-proof-reader"},"Contributing as a translator or proof-reader"),(0,o.kt)("p",null,"We use the ",(0,o.kt)("a",{parentName:"p",href:"https://crowdin.com"},"Crowdin")," web platform to translate Richie to different languages.\nAll translations are hosted at ",(0,o.kt)("a",{parentName:"p",href:"https://i18n.richie.education"},"https://i18n.richie.education"),", which allows translators and\nproof-readers to contribute on translations in the languages they master."),(0,o.kt)("h3",{id:"sign-up-on-crowdin"},"Sign-up on Crowdin"),(0,o.kt)("p",null,"If you don't have an account on Crowdin already, go to ",(0,o.kt)("a",{parentName:"p",href:"https://accounts.crowdin.com/register"},"https://accounts.crowdin.com/register")," and\nfill out the form to create a free account."),(0,o.kt)("h3",{id:"join-the-richie-project"},"Join the Richie project"),(0,o.kt)("p",null,"Now that you have an account on Crowdin,\n",(0,o.kt)("a",{parentName:"p",href:"https://crowdin.com/project/richie"},'look for the project called "Richie"'),', select the language\non which you wish to contribute and click the "Join" button as demonstrated below:'),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"How to join Richie on Crowdin",src:n(50792).Z,width:"2462",height:"1506"})),(0,o.kt)("p",null,"We will then review you application and you should soon start translating strings!"),(0,o.kt)("p",null,"For more information on how Crowdin works, you can refer to\n",(0,o.kt)("a",{parentName:"p",href:"https://support.crowdin.com"},"their documentation"),"."),(0,o.kt)("h3",{id:"add-a-new-language"},"Add a new language"),(0,o.kt)("p",null,'If Richie is not yet translated in the language you want, let us know by clicking the "contact"\nlink on ',(0,o.kt)("a",{parentName:"p",href:"https://i18n.richie.education"},"Richie's Crowdin profile page")," and we will consider adding\nit."),(0,o.kt)("p",null,"If you request a new language, the Richie community will expect you to keep this language\nup-to-date each time strings are modified or new strings are added, and this before each\nrelease."),(0,o.kt)("p",null,"Before asking for a new language, make sure it does not already exist. If your language already\nexists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider\ncontributing on the existing language if your resources to contribute are limited."))}h.isMDXComponent=!0},50792:(e,t,n)=>{n.d(t,{Z:()=>r});const r=n.p+"assets/images/crowdin-join-richie-fd505b1a132bafb2bdafc715649a7c0f.gif"}}]); \ No newline at end of file diff --git a/assets/js/2fbb3042.6321f6cb.js b/assets/js/2fbb3042.6321f6cb.js deleted file mode 100644 index 4f82123412..0000000000 --- a/assets/js/2fbb3042.6321f6cb.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[46333],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>h});var a=n(67294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function l(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var s=a.createContext({}),p=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},u=function(e){var t=p(e.components);return a.createElement(s.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},c=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,l=e.originalType,s=e.parentName,u=o(e,["components","mdxType","originalType","parentName"]),c=p(n),h=r,m=c["".concat(s,".").concat(h)]||c[h]||d[h]||l;return n?a.createElement(m,i(i({ref:t},u),{},{components:n})):a.createElement(m,i({ref:t},u))}));function h(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var l=n.length,i=new Array(l);i[0]=c;var o={};for(var s in t)hasOwnProperty.call(t,s)&&(o[s]=t[s]);o.originalType=e,o.mdxType="string"==typeof e?e:r,i[1]=o;for(var p=2;p{n.r(t),n.d(t,{assets:()=>u,contentTitle:()=>s,default:()=>h,frontMatter:()=>o,metadata:()=>p,toc:()=>d});var a=n(83117),r=n(80102),l=(n(67294),n(3905)),i=["components"],o={id:"native-installation",title:"Installing Richie on your machine",sidebar_label:"Native installation"},s=void 0,p={unversionedId:"native-installation",id:"version-2.21.1/native-installation",title:"Installing Richie on your machine",description:"This document aims to list all needed steps to have a working Richie",source:"@site/versioned_docs/version-2.21.1/native-installation.md",sourceDirName:".",slug:"/native-installation",permalink:"/docs/native-installation",draft:!1,tags:[],version:"2.21.1",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1680624606,formattedLastUpdatedAt:"Apr 4, 2023",frontMatter:{id:"native-installation",title:"Installing Richie on your machine",sidebar_label:"Native installation"},sidebar:"docs",previous:{title:"Docker development",permalink:"/docs/docker-development"},next:{title:"Contributing guide",permalink:"/docs/contributing-guide"}},u={},d=[{value:"Installing a fresh server",id:"installing-a-fresh-server",level:2},{value:"Version",id:"version",level:3},{value:"System update",id:"system-update",level:3},{value:"Database part",id:"database-part",level:2},{value:"Elasticsearch",id:"elasticsearch",level:2},{value:"Ubuntu",id:"ubuntu",level:3},{value:"OS X",id:"os-x",level:3},{value:"Application part",id:"application-part",level:2},{value:"Python and other requirements",id:"python-and-other-requirements",level:3},{value:"The virtualenv",id:"the-virtualenv",level:3},{value:"Frontend build",id:"frontend-build",level:3},{value:"Run server",id:"run-server",level:3}],c={toc:d};function h(e){var t=e.components,n=(0,r.Z)(e,i);return(0,l.kt)("wrapper",(0,a.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,l.kt)("p",null,"This document aims to list all needed steps to have a working ",(0,l.kt)("inlineCode",{parentName:"p"},"Richie"),"\ninstallation on your laptop."),(0,l.kt)("p",null,"A better approach is to use ",(0,l.kt)("a",{parentName:"p",href:"https://docs.docker.com"},(0,l.kt)("inlineCode",{parentName:"a"},"Docker"))," as explained in\nour guide for ",(0,l.kt)("a",{parentName:"p",href:"/docs/installation"},"container-native installation")," instructions."),(0,l.kt)("h2",{id:"installing-a-fresh-server"},"Installing a fresh server"),(0,l.kt)("h3",{id:"version"},"Version"),(0,l.kt)("p",null,"You need a ",(0,l.kt)("inlineCode",{parentName:"p"},"Ubuntu 18.04 Bionic Beaver")," (the latest LTS version) fresh\ninstallation."),(0,l.kt)("p",null,"If you are using another operating system or distribution, you can use\n",(0,l.kt)("a",{parentName:"p",href:"https://docs.vagrantup.com/v2/getting-started/index.html"},(0,l.kt)("inlineCode",{parentName:"a"},"Vagrant"))," to get a\nrunning Ubuntu 18.04 server in seconds."),(0,l.kt)("h3",{id:"system-update"},"System update"),(0,l.kt)("p",null,"Be sure to have fresh packages on the server (kernel, libc, ssl patches...):\npost"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-sh"},"sudo apt-get -y update\nsudo apt-get -y dist-upgrade\n")),(0,l.kt)("h2",{id:"database-part"},"Database part"),(0,l.kt)("p",null,"You must first install ",(0,l.kt)("inlineCode",{parentName:"p"},"postgresql"),"."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-sh"},"// On Linux\nsudo apt-get -y install postgresql\n\n// On OS X\nbrew install postgresql@10\nbrew services start postgresql@10\n// don't forget to add your new postgres install to the $PATH\n")),(0,l.kt)("p",null,(0,l.kt)("inlineCode",{parentName:"p"},"Postgresql")," is now running."),(0,l.kt)("p",null,"Then you can create the database owner and the database itself, using the\n",(0,l.kt)("inlineCode",{parentName:"p"},"postgres")," user:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-sh"},"sudo -u postgres -i // skip this on OS X as the default install will use your local user\ncreateuser fun -sP\n")),(0,l.kt)("p",null,"Note: we created the user as a superuser. This should only be done in dev/test\nenvironments."),(0,l.kt)("p",null,"Now, create the database with this user:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-sh"},"createdb richie -O fun -W\nexit\n")),(0,l.kt)("h2",{id:"elasticsearch"},"Elasticsearch"),(0,l.kt)("h3",{id:"ubuntu"},"Ubuntu"),(0,l.kt)("p",null,"Download and install the Public Signing Key"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"$ wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -\n")),(0,l.kt)("p",null,"You may need to install the apt-transport-https package on Debian before\nproceeding:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"$ sudo apt-get install apt-transport-https\n")),(0,l.kt)("p",null,"Save the repository definition to /etc/apt/sources.list.d/elastic-6.3.1.list:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},'$ echo "deb https://artifacts.elastic.co/packages/6.3.1/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-6.3.1.list\n')),(0,l.kt)("p",null,"Update repository and install"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"$ sudo apt-get update\n$ sudo apt-get install elasticsearch\n$ sudo /etc/init.d/elasticsearch start\n")),(0,l.kt)("h3",{id:"os-x"},"OS X"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"$ brew install elasticsearch\n")),(0,l.kt)("h2",{id:"application-part"},"Application part"),(0,l.kt)("h3",{id:"python-and-other-requirements"},"Python and other requirements"),(0,l.kt)("p",null,"We use ",(0,l.kt)("inlineCode",{parentName:"p"},"Python 3.6")," which is the one installed by default in ",(0,l.kt)("inlineCode",{parentName:"p"},"Ubuntu 18.04"),"."),(0,l.kt)("p",null,"You can install it on OS X using the following commands. Make sure to always run\n",(0,l.kt)("inlineCode",{parentName:"p"},"python3")," instead of ",(0,l.kt)("inlineCode",{parentName:"p"},"python")," and ",(0,l.kt)("inlineCode",{parentName:"p"},"pip3")," instead of ",(0,l.kt)("inlineCode",{parentName:"p"},"pip")," to ensure the correct\nversion of Python (your homebrew install of 3) is used."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"brew install python3\nbrew postinstall python3\n")),(0,l.kt)("h3",{id:"the-virtualenv"},"The virtualenv"),(0,l.kt)("p",null,"Place yourself in the application directory ",(0,l.kt)("inlineCode",{parentName:"p"},"app"),":"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"cd app\n")),(0,l.kt)("p",null,"We choose to run our application in a virtual environment."),(0,l.kt)("p",null,"For this, we'll install ",(0,l.kt)("inlineCode",{parentName:"p"},"virtualenvwrapper")," and add an environment:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"pip install virtualenvwrapper\n")),(0,l.kt)("p",null,"You can open a new shell to activate the virtualenvwrapper commands, or simply\ndo:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"source $(which virtualenvwrapper.sh)\n")),(0,l.kt)("p",null,"Then create the virtual environment for ",(0,l.kt)("inlineCode",{parentName:"p"},"richie"),":"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"mkvirtualenv richie --no-site-packages --python=python3\n")),(0,l.kt)("p",null,"The virtualenv should now be activated and you can install the Python\ndependencies for development:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"pip install -e .[dev]\n")),(0,l.kt)("p",null,'The "dev.txt" requirement file installs packages specific to a dev environment\nand should not be used in production.'),(0,l.kt)("h3",{id:"frontend-build"},"Frontend build"),(0,l.kt)("p",null,"This project is a hybrid that uses both Django generated pages and frontend JS\ncode. As such, it includes a frontend build process that comes in two parts: JS\n& CSS."),(0,l.kt)("p",null,"We need NPM to install the dependencies and run the build, which depends on a\nversion of Nodejs specified in ",(0,l.kt)("inlineCode",{parentName:"p"},".nvmrc"),". See ",(0,l.kt)("a",{parentName:"p",href:"https://github.com/creationix/nvm"},"the\nrepo")," for instructions on how to install NVM.\nTo take advantage of ",(0,l.kt)("inlineCode",{parentName:"p"},".nvmrc"),", run this in the context of the repository:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"nvm install\nnvm use\n")),(0,l.kt)("p",null,"As a prerequisite to running the frontend build for either JS or CSS, you'll\nneed to ",(0,l.kt)("a",{parentName:"p",href:"https://yarnpkg.com/lang/en/docs/install/"},"install yarn")," and download\ndependencies ",(0,l.kt)("em",{parentName:"p"},"via"),":"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"yarn install\n")),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},"JS build")),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"npm run build\n")),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},"CSS build")),(0,l.kt)("p",null,"This will compile all our SCSS files into one bundle and put it in the static\nfolder we're serving."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"npm run sass\n")),(0,l.kt)("h3",{id:"run-server"},"Run server"),(0,l.kt)("p",null,"Make sure your database is up-to-date before running the application the first\ntime and after each modification to your models:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"python sandbox/manage.py migrate\n")),(0,l.kt)("p",null,"You can create a superuser account:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"python sandbox/manage.py createsuperuser\n")),(0,l.kt)("p",null,"Run the tests"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"python sandbox/manage.py test\n")),(0,l.kt)("p",null,"You should now be able to start Django and view the site at\n",(0,l.kt)("a",{parentName:"p",href:"http://localhost:8000"},"localhost:8000")),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"python sandbox/manage.py runserver\n")))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/2fbb3042.b08d134d.js b/assets/js/2fbb3042.b08d134d.js new file mode 100644 index 0000000000..f26101bbc1 --- /dev/null +++ b/assets/js/2fbb3042.b08d134d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[46333],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>h});var a=n(67294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function l(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var s=a.createContext({}),p=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},u=function(e){var t=p(e.components);return a.createElement(s.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},c=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,l=e.originalType,s=e.parentName,u=o(e,["components","mdxType","originalType","parentName"]),c=p(n),h=r,m=c["".concat(s,".").concat(h)]||c[h]||d[h]||l;return n?a.createElement(m,i(i({ref:t},u),{},{components:n})):a.createElement(m,i({ref:t},u))}));function h(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var l=n.length,i=new Array(l);i[0]=c;var o={};for(var s in t)hasOwnProperty.call(t,s)&&(o[s]=t[s]);o.originalType=e,o.mdxType="string"==typeof e?e:r,i[1]=o;for(var p=2;p{n.r(t),n.d(t,{assets:()=>u,contentTitle:()=>s,default:()=>h,frontMatter:()=>o,metadata:()=>p,toc:()=>d});var a=n(83117),r=n(80102),l=(n(67294),n(3905)),i=["components"],o={id:"native-installation",title:"Installing Richie on your machine",sidebar_label:"Native installation"},s=void 0,p={unversionedId:"native-installation",id:"version-2.21.1/native-installation",title:"Installing Richie on your machine",description:"This document aims to list all needed steps to have a working Richie",source:"@site/versioned_docs/version-2.21.1/native-installation.md",sourceDirName:".",slug:"/native-installation",permalink:"/docs/2.21.1/native-installation",draft:!1,tags:[],version:"2.21.1",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1680624606,formattedLastUpdatedAt:"Apr 4, 2023",frontMatter:{id:"native-installation",title:"Installing Richie on your machine",sidebar_label:"Native installation"},sidebar:"docs",previous:{title:"Docker development",permalink:"/docs/2.21.1/docker-development"},next:{title:"Contributing guide",permalink:"/docs/2.21.1/contributing-guide"}},u={},d=[{value:"Installing a fresh server",id:"installing-a-fresh-server",level:2},{value:"Version",id:"version",level:3},{value:"System update",id:"system-update",level:3},{value:"Database part",id:"database-part",level:2},{value:"Elasticsearch",id:"elasticsearch",level:2},{value:"Ubuntu",id:"ubuntu",level:3},{value:"OS X",id:"os-x",level:3},{value:"Application part",id:"application-part",level:2},{value:"Python and other requirements",id:"python-and-other-requirements",level:3},{value:"The virtualenv",id:"the-virtualenv",level:3},{value:"Frontend build",id:"frontend-build",level:3},{value:"Run server",id:"run-server",level:3}],c={toc:d};function h(e){var t=e.components,n=(0,r.Z)(e,i);return(0,l.kt)("wrapper",(0,a.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,l.kt)("p",null,"This document aims to list all needed steps to have a working ",(0,l.kt)("inlineCode",{parentName:"p"},"Richie"),"\ninstallation on your laptop."),(0,l.kt)("p",null,"A better approach is to use ",(0,l.kt)("a",{parentName:"p",href:"https://docs.docker.com"},(0,l.kt)("inlineCode",{parentName:"a"},"Docker"))," as explained in\nour guide for ",(0,l.kt)("a",{parentName:"p",href:"/docs/2.21.1/installation"},"container-native installation")," instructions."),(0,l.kt)("h2",{id:"installing-a-fresh-server"},"Installing a fresh server"),(0,l.kt)("h3",{id:"version"},"Version"),(0,l.kt)("p",null,"You need a ",(0,l.kt)("inlineCode",{parentName:"p"},"Ubuntu 18.04 Bionic Beaver")," (the latest LTS version) fresh\ninstallation."),(0,l.kt)("p",null,"If you are using another operating system or distribution, you can use\n",(0,l.kt)("a",{parentName:"p",href:"https://docs.vagrantup.com/v2/getting-started/index.html"},(0,l.kt)("inlineCode",{parentName:"a"},"Vagrant"))," to get a\nrunning Ubuntu 18.04 server in seconds."),(0,l.kt)("h3",{id:"system-update"},"System update"),(0,l.kt)("p",null,"Be sure to have fresh packages on the server (kernel, libc, ssl patches...):\npost"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-sh"},"sudo apt-get -y update\nsudo apt-get -y dist-upgrade\n")),(0,l.kt)("h2",{id:"database-part"},"Database part"),(0,l.kt)("p",null,"You must first install ",(0,l.kt)("inlineCode",{parentName:"p"},"postgresql"),"."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-sh"},"// On Linux\nsudo apt-get -y install postgresql\n\n// On OS X\nbrew install postgresql@10\nbrew services start postgresql@10\n// don't forget to add your new postgres install to the $PATH\n")),(0,l.kt)("p",null,(0,l.kt)("inlineCode",{parentName:"p"},"Postgresql")," is now running."),(0,l.kt)("p",null,"Then you can create the database owner and the database itself, using the\n",(0,l.kt)("inlineCode",{parentName:"p"},"postgres")," user:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-sh"},"sudo -u postgres -i // skip this on OS X as the default install will use your local user\ncreateuser fun -sP\n")),(0,l.kt)("p",null,"Note: we created the user as a superuser. This should only be done in dev/test\nenvironments."),(0,l.kt)("p",null,"Now, create the database with this user:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-sh"},"createdb richie -O fun -W\nexit\n")),(0,l.kt)("h2",{id:"elasticsearch"},"Elasticsearch"),(0,l.kt)("h3",{id:"ubuntu"},"Ubuntu"),(0,l.kt)("p",null,"Download and install the Public Signing Key"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"$ wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -\n")),(0,l.kt)("p",null,"You may need to install the apt-transport-https package on Debian before\nproceeding:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"$ sudo apt-get install apt-transport-https\n")),(0,l.kt)("p",null,"Save the repository definition to /etc/apt/sources.list.d/elastic-6.3.1.list:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},'$ echo "deb https://artifacts.elastic.co/packages/6.3.1/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-6.3.1.list\n')),(0,l.kt)("p",null,"Update repository and install"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"$ sudo apt-get update\n$ sudo apt-get install elasticsearch\n$ sudo /etc/init.d/elasticsearch start\n")),(0,l.kt)("h3",{id:"os-x"},"OS X"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"$ brew install elasticsearch\n")),(0,l.kt)("h2",{id:"application-part"},"Application part"),(0,l.kt)("h3",{id:"python-and-other-requirements"},"Python and other requirements"),(0,l.kt)("p",null,"We use ",(0,l.kt)("inlineCode",{parentName:"p"},"Python 3.6")," which is the one installed by default in ",(0,l.kt)("inlineCode",{parentName:"p"},"Ubuntu 18.04"),"."),(0,l.kt)("p",null,"You can install it on OS X using the following commands. Make sure to always run\n",(0,l.kt)("inlineCode",{parentName:"p"},"python3")," instead of ",(0,l.kt)("inlineCode",{parentName:"p"},"python")," and ",(0,l.kt)("inlineCode",{parentName:"p"},"pip3")," instead of ",(0,l.kt)("inlineCode",{parentName:"p"},"pip")," to ensure the correct\nversion of Python (your homebrew install of 3) is used."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"brew install python3\nbrew postinstall python3\n")),(0,l.kt)("h3",{id:"the-virtualenv"},"The virtualenv"),(0,l.kt)("p",null,"Place yourself in the application directory ",(0,l.kt)("inlineCode",{parentName:"p"},"app"),":"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"cd app\n")),(0,l.kt)("p",null,"We choose to run our application in a virtual environment."),(0,l.kt)("p",null,"For this, we'll install ",(0,l.kt)("inlineCode",{parentName:"p"},"virtualenvwrapper")," and add an environment:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"pip install virtualenvwrapper\n")),(0,l.kt)("p",null,"You can open a new shell to activate the virtualenvwrapper commands, or simply\ndo:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"source $(which virtualenvwrapper.sh)\n")),(0,l.kt)("p",null,"Then create the virtual environment for ",(0,l.kt)("inlineCode",{parentName:"p"},"richie"),":"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"mkvirtualenv richie --no-site-packages --python=python3\n")),(0,l.kt)("p",null,"The virtualenv should now be activated and you can install the Python\ndependencies for development:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"pip install -e .[dev]\n")),(0,l.kt)("p",null,'The "dev.txt" requirement file installs packages specific to a dev environment\nand should not be used in production.'),(0,l.kt)("h3",{id:"frontend-build"},"Frontend build"),(0,l.kt)("p",null,"This project is a hybrid that uses both Django generated pages and frontend JS\ncode. As such, it includes a frontend build process that comes in two parts: JS\n& CSS."),(0,l.kt)("p",null,"We need NPM to install the dependencies and run the build, which depends on a\nversion of Nodejs specified in ",(0,l.kt)("inlineCode",{parentName:"p"},".nvmrc"),". See ",(0,l.kt)("a",{parentName:"p",href:"https://github.com/creationix/nvm"},"the\nrepo")," for instructions on how to install NVM.\nTo take advantage of ",(0,l.kt)("inlineCode",{parentName:"p"},".nvmrc"),", run this in the context of the repository:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"nvm install\nnvm use\n")),(0,l.kt)("p",null,"As a prerequisite to running the frontend build for either JS or CSS, you'll\nneed to ",(0,l.kt)("a",{parentName:"p",href:"https://yarnpkg.com/lang/en/docs/install/"},"install yarn")," and download\ndependencies ",(0,l.kt)("em",{parentName:"p"},"via"),":"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"yarn install\n")),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},"JS build")),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"npm run build\n")),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},"CSS build")),(0,l.kt)("p",null,"This will compile all our SCSS files into one bundle and put it in the static\nfolder we're serving."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"npm run sass\n")),(0,l.kt)("h3",{id:"run-server"},"Run server"),(0,l.kt)("p",null,"Make sure your database is up-to-date before running the application the first\ntime and after each modification to your models:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"python sandbox/manage.py migrate\n")),(0,l.kt)("p",null,"You can create a superuser account:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"python sandbox/manage.py createsuperuser\n")),(0,l.kt)("p",null,"Run the tests"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"python sandbox/manage.py test\n")),(0,l.kt)("p",null,"You should now be able to start Django and view the site at\n",(0,l.kt)("a",{parentName:"p",href:"http://localhost:8000"},"localhost:8000")),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"python sandbox/manage.py runserver\n")))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/34ff41bb.c7ff2118.js b/assets/js/34ff41bb.c7ff2118.js new file mode 100644 index 0000000000..7b730d9000 --- /dev/null +++ b/assets/js/34ff41bb.c7ff2118.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[19176],{3905:(e,n,t)=>{t.d(n,{Zo:()=>u,kt:()=>h});var a=t(67294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function o(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var s=a.createContext({}),p=function(e){var n=a.useContext(s),t=n;return e&&(t="function"==typeof e?e(n):o(o({},n),e)),t},u=function(e){var n=p(e.components);return a.createElement(s.Provider,{value:n},e.children)},c={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},d=a.forwardRef((function(e,n){var t=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),d=p(t),h=i,m=d["".concat(s,".").concat(h)]||d[h]||c[h]||r;return t?a.createElement(m,o(o({ref:n},u),{},{components:t})):a.createElement(m,o({ref:n},u))}));function h(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var r=t.length,o=new Array(r);o[0]=d;var l={};for(var s in n)hasOwnProperty.call(n,s)&&(l[s]=n[s]);l.originalType=e,l.mdxType="string"==typeof e?e:i,o[1]=l;for(var p=2;p{t.r(n),t.d(n,{assets:()=>u,contentTitle:()=>s,default:()=>h,frontMatter:()=>l,metadata:()=>p,toc:()=>c});var a=t(83117),i=t(80102),r=(t(67294),t(3905)),o=["components"],l={id:"displaying-connection-status",title:"Displaying OpenEdX connection status in Richie",sidebar_label:"Displaying connection status"},s=void 0,p={unversionedId:"displaying-connection-status",id:"version-2.22.0/displaying-connection-status",title:"Displaying OpenEdX connection status in Richie",description:"This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status",source:"@site/versioned_docs/version-2.22.0/displaying-connection-status.md",sourceDirName:".",slug:"/displaying-connection-status",permalink:"/docs/displaying-connection-status",draft:!1,tags:[],version:"2.22.0",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1682069684,formattedLastUpdatedAt:"Apr 21, 2023",frontMatter:{id:"displaying-connection-status",title:"Displaying OpenEdX connection status in Richie",sidebar_label:"Displaying connection status"}},u={},c=[{value:"Prerequisites",id:"prerequisites",level:2},{value:"Allow redirects",id:"allow-redirects",level:2},{value:"Configure authentication delegation",id:"configure-authentication-delegation",level:2},{value:"BASE_URL",id:"base_url",level:3},{value:"BACKEND",id:"backend",level:3},{value:"PROFILE_URLS",id:"profile_urls",level:3}],d={toc:c};function h(e){var n=e.components,t=(0,i.Z)(e,o);return(0,r.kt)("wrapper",(0,a.Z)({},d,t,{components:n,mdxType:"MDXLayout"}),(0,r.kt)("p",null,"This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status\nand display profile information for the logged-in user in Richie."),(0,r.kt)("p",null,"In Richie, the only users that are actually authenticated on the DjangoCMS instance producing the\nsite, are editors and staff users. Your instructors and learners will not have user accounts on\nRichie, but authentication is delegated to a remote service that can be OpenEdX, KeyCloack, or\nyour own centralized identity management service."),(0,r.kt)("p",null,"In the following, we will explain how to use OpenEdX as your authentication delegation service."),(0,r.kt)("h2",{id:"prerequisites"},"Prerequisites"),(0,r.kt)("p",null,"Richie will need to make CORS requests to the OpenEdX instance. As a consequence, you need to\nactivate CORS requests on your OpenEdX instance:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-python"},'FEATURES = {\n ...\n "ENABLE_CORS_HEADERS": True,\n}\n')),(0,r.kt)("p",null,"Then, make sure the following settings are set as follows on your OpenEdX instance:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-python"},'CORS_ALLOW_CREDENTIALS = True\nCORS_ALLOW_INSECURE = False\nCORS_ORIGIN_ALLOW_ALL = False\nCORS_ORIGIN_WHITELIST: ["richie.example.com"] # The domain on which Richie is hosted\n')),(0,r.kt)("h2",{id:"allow-redirects"},"Allow redirects"),(0,r.kt)("p",null,"When Richie sends the user to the OpenEdX instance for authentication, and wants OpenEdX to\nredirect the user back to Richie after a successful login or signup, it prefixes the path with\n",(0,r.kt)("inlineCode",{parentName:"p"},"/richie"),". Adding the following rule to your Nginx server (or equivalent) and replacing the\nrichie host by yours will allow this redirect to follow through to your Richie instance:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre"},"rewrite ^/richie/(.*)$ https://richie.example.com/$1 permanent;\n")),(0,r.kt)("h2",{id:"configure-authentication-delegation"},"Configure authentication delegation"),(0,r.kt)("p",null,"Now, on your Richie instance, you need to configure the service to which Richie will delegate\nauthentication using the ",(0,r.kt)("inlineCode",{parentName:"p"},"RICHIE_AUTHENTICATION_DELEGATION")," setting:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-python"},'RICHIE_AUTHENTICATION_DELEGATION = {\n "BASE_URL": "https://lms.example.com",\n "BACKEND": "openedx-hawthorn",\n "PROFILE_URLS": {\n "dashboard": {\n "label": _("Dashboard"),\n "href": "{base_url:s}/dashboard",\n },\n },\n}\n')),(0,r.kt)("p",null,"The following should help you understand how to configure this setting:"),(0,r.kt)("h3",{id:"base_url"},"BASE_URL"),(0,r.kt)("p",null,"The base url on which the OpenEdX instance is hosted. This is used to construct the complete url\nof the login/signup pages to which the frontend application will send the user for authentication."),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Type: string"),(0,r.kt)("li",{parentName:"ul"},"Required: Yes"),(0,r.kt)("li",{parentName:"ul"},"Value: for example ",(0,r.kt)("a",{parentName:"li",href:"https://lms.example.com"},"https://lms.example.com"))),(0,r.kt)("h3",{id:"backend"},"BACKEND"),(0,r.kt)("p",null,"The name of the ReactJS backend to use for the targeted LMS."),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Type: string"),(0,r.kt)("li",{parentName:"ul"},"Required: Yes"),(0,r.kt)("li",{parentName:"ul"},"Value: Richie ships with the following Javascript backends:",(0,r.kt)("ul",{parentName:"li"},(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"openedx-dogwood"),": backend for OpenEdX versions equal to ",(0,r.kt)("inlineCode",{parentName:"li"},"dogwood")," or ",(0,r.kt)("inlineCode",{parentName:"li"},"eucalyptus")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"openedx-hawthorn"),": backend for OpenEdX versions equal to ",(0,r.kt)("inlineCode",{parentName:"li"},"hawthorn")," or higher"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"openedx-fonzie"),": backend for OpenEdX via ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/openfun/fonzie"},"Fonzie"),"\n(extra user info and JWT tokens)"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"base"),": fake backend for development purposes")))),(0,r.kt)("h3",{id:"profile_urls"},"PROFILE_URLS"),(0,r.kt)("p",null,"Mapping definition of custom links presented to the logged-in user as a dropdown menu when he/she\nclicks on his/her username in Richie's page header."),(0,r.kt)("p",null,"Links order will be respected to build the dropdown menu."),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},"Type: dictionary")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},"Required: No")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("p",{parentName:"li"},"Value: For example, to emulate the links proposed in OpenEdX, you can configure this setting\nas follows:"),(0,r.kt)("pre",{parentName:"li"},(0,r.kt)("code",{parentName:"pre",className:"language-python"},' {\n "dashboard": {\n "label": _("Dashboard"),\n "href": "{base_url:s}/dashboard",\n },\n "profile": {\n "label": _("Profile"),\n "href": "{base_url:s}/u/(username)",\n },\n "account": {\n "label": _("Account"),\n "href": "{base_url:s}/account/settings",\n }\n }\n')),(0,r.kt)("p",{parentName:"li"}," The ",(0,r.kt)("inlineCode",{parentName:"p"},"base_url")," variable is used as a Python format parameter and will be replaced by the value\nset for the above authentication delegation ",(0,r.kt)("inlineCode",{parentName:"p"},"BASE_URL")," setting."),(0,r.kt)("p",{parentName:"li"}," If you need to bind user data into a url, wrap the property between brackets. For example, the\nlink configured above for the profile page ",(0,r.kt)("inlineCode",{parentName:"p"},"{base_url:s}/u/(username)")," would point to\n",(0,r.kt)("inlineCode",{parentName:"p"},"https://lms.example.com/u/johndoe")," for a user carrying the username ",(0,r.kt)("inlineCode",{parentName:"p"},"johndoe"),"."))))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/d1239249.cbc8ccc8.js b/assets/js/378f3d86.4c7d379d.js similarity index 95% rename from assets/js/d1239249.cbc8ccc8.js rename to assets/js/378f3d86.4c7d379d.js index 2521e0e1ed..997947aad2 100644 --- a/assets/js/d1239249.cbc8ccc8.js +++ b/assets/js/378f3d86.4c7d379d.js @@ -1 +1 @@ -"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[52291],{43673:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"2.21.1","label":"2.21.1","banner":null,"badge":true,"noIndex":false,"className":"docs-version-2.21.1","isLast":true,"docsSidebars":{"docs":[{"type":"category","label":"Getting started","items":[{"type":"link","label":"Discover Richie","href":"/docs/discover","docId":"discover"},{"type":"link","label":"Start your own site","href":"/docs/cookiecutter","docId":"cookiecutter"}],"collapsed":true,"collapsible":true},{"type":"category","label":"Recipes","items":[{"type":"link","label":"Search filters customization","href":"/docs/filters-customization","docId":"filters-customization"},{"type":"link","label":"Django & React","href":"/docs/django-react-interop","docId":"django-react-interop"},{"type":"link","label":"Building the frontend","href":"/docs/building-the-frontend","docId":"building-the-frontend"},{"type":"link","label":"Frontend overrides","href":"/docs/frontend-overrides","docId":"frontend-overrides"},{"type":"link","label":"I18n","href":"/docs/internationalization","docId":"internationalization"},{"type":"link","label":"LMS connection","href":"/docs/lms-connection","docId":"lms-connection"},{"type":"link","label":"Web Analytics","href":"/docs/web-analytics","docId":"web-analytics"}],"collapsed":true,"collapsible":true},{"type":"category","label":"Contributing","items":[{"type":"link","label":"Installation","href":"/docs/installation","docId":"installation"},{"type":"link","label":"Docker development","href":"/docs/docker-development","docId":"docker-development"},{"type":"link","label":"Native installation","href":"/docs/native-installation","docId":"native-installation"},{"type":"link","label":"Contributing guide","href":"/docs/contributing-guide","docId":"contributing-guide"},{"type":"link","label":"Accessibility testing","href":"/docs/accessibility-testing","docId":"accessibility-testing"},{"type":"link","label":"CSS Guidelines","href":"/docs/css-guidelines","docId":"css-guidelines"}],"collapsed":true,"collapsible":true}]},"docs":{"accessibility-testing":{"id":"accessibility-testing","title":"Automated accessibility checks","description":"Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.","sidebar":"docs"},"api/course-run-synchronization-api":{"id":"api/course-run-synchronization-api","title":"Course run synchronization API","description":"API endpoint allowing remote systems to synchronize their course runs with a Richie instance."},"building-the-frontend":{"id":"building-the-frontend","title":"Building Richie\'s frontend in your own project","description":"Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.","sidebar":"docs"},"contributing-guide":{"id":"contributing-guide","title":"Contributing guide","description":"This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.","sidebar":"docs"},"cookiecutter":{"id":"cookiecutter","title":"Start your own site","description":"We use Cookiecutter to help you","sidebar":"docs"},"css-guidelines":{"id":"css-guidelines","title":"CSS Guidelines","description":"The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.","sidebar":"docs"},"discover":{"id":"discover","title":"Discover Richie","description":"Learning Management Systems (LMS) are great tools for hosting and playing interactive online","sidebar":"docs"},"displaying-connection-status":{"id":"displaying-connection-status","title":"Displaying OpenEdX connection status in Richie","description":"This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status"},"django-react-interop":{"id":"django-react-interop","title":"Connecting React components with Django","description":"richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.","sidebar":"docs"},"docker-development":{"id":"docker-development","title":"Developing Richie with Docker","description":"Now that you have Richie up and running, you can start working with it.","sidebar":"docs"},"filters-customization":{"id":"filters-customization","title":"Customizing search filters","description":"You may want to customize the filters on the left side bar of the search page.","sidebar":"docs"},"frontend-overrides":{"id":"frontend-overrides","title":"Overriding frontend components","description":"Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.","sidebar":"docs"},"installation":{"id":"installation","title":"Installing Richie for development","description":"Richie is a container-native application but can also be installed","sidebar":"docs"},"internationalization":{"id":"internationalization","title":"Internationalization","description":"richie has built-in localization and internationalization:","sidebar":"docs"},"joanie-connection":{"id":"joanie-connection","title":"Joanie Connection","description":"Joanie delivers an API able to manage course"},"lms-backends":{"id":"lms-backends","title":"Configuring LMS Backends","description":"Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless"},"lms-connection":{"id":"lms-connection","title":"Connecting Richie with one or more LMS","description":"Connecting Richie to an LMS","sidebar":"docs"},"native-installation":{"id":"native-installation","title":"Installing Richie on your machine","description":"This document aims to list all needed steps to have a working Richie","sidebar":"docs"},"synchronizing-course-runs":{"id":"synchronizing-course-runs","title":"Synchronizing course runs between Richie and OpenEdX","description":"Richie can receive automatic course runs updates on a dedicated API endpoint."},"tls-connection":{"id":"tls-connection","title":"Connecting Richie and OpenEdX over TLS for development","description":"Purpose"},"web-analytics":{"id":"web-analytics","title":"Add web analytics to your site","description":"Richie has native support to Google Universal Analytics and Google Tag Manager Web Analytics solutions.","sidebar":"docs"}}}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[82698],{26807:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"2.22.0","label":"2.22.0","banner":null,"badge":true,"noIndex":false,"className":"docs-version-2.22.0","isLast":true,"docsSidebars":{"docs":[{"type":"category","label":"Getting started","items":[{"type":"link","label":"Discover Richie","href":"/docs/discover","docId":"discover"},{"type":"link","label":"Start your own site","href":"/docs/cookiecutter","docId":"cookiecutter"}],"collapsed":true,"collapsible":true},{"type":"category","label":"Recipes","items":[{"type":"link","label":"Search filters customization","href":"/docs/filters-customization","docId":"filters-customization"},{"type":"link","label":"Django & React","href":"/docs/django-react-interop","docId":"django-react-interop"},{"type":"link","label":"Building the frontend","href":"/docs/building-the-frontend","docId":"building-the-frontend"},{"type":"link","label":"Frontend overrides","href":"/docs/frontend-overrides","docId":"frontend-overrides"},{"type":"link","label":"I18n","href":"/docs/internationalization","docId":"internationalization"},{"type":"link","label":"LMS connection","href":"/docs/lms-connection","docId":"lms-connection"},{"type":"link","label":"Web Analytics","href":"/docs/web-analytics","docId":"web-analytics"}],"collapsed":true,"collapsible":true},{"type":"category","label":"Contributing","items":[{"type":"link","label":"Installation","href":"/docs/installation","docId":"installation"},{"type":"link","label":"Docker development","href":"/docs/docker-development","docId":"docker-development"},{"type":"link","label":"Native installation","href":"/docs/native-installation","docId":"native-installation"},{"type":"link","label":"Contributing guide","href":"/docs/contributing-guide","docId":"contributing-guide"},{"type":"link","label":"Accessibility testing","href":"/docs/accessibility-testing","docId":"accessibility-testing"},{"type":"link","label":"CSS Guidelines","href":"/docs/css-guidelines","docId":"css-guidelines"}],"collapsed":true,"collapsible":true}]},"docs":{"accessibility-testing":{"id":"accessibility-testing","title":"Automated accessibility checks","description":"Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.","sidebar":"docs"},"api/course-run-synchronization-api":{"id":"api/course-run-synchronization-api","title":"Course run synchronization API","description":"API endpoint allowing remote systems to synchronize their course runs with a Richie instance."},"building-the-frontend":{"id":"building-the-frontend","title":"Building Richie\'s frontend in your own project","description":"Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.","sidebar":"docs"},"contributing-guide":{"id":"contributing-guide","title":"Contributing guide","description":"This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.","sidebar":"docs"},"cookiecutter":{"id":"cookiecutter","title":"Start your own site","description":"We use Cookiecutter to help you","sidebar":"docs"},"css-guidelines":{"id":"css-guidelines","title":"CSS Guidelines","description":"The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.","sidebar":"docs"},"discover":{"id":"discover","title":"Discover Richie","description":"Learning Management Systems (LMS) are great tools for hosting and playing interactive online","sidebar":"docs"},"displaying-connection-status":{"id":"displaying-connection-status","title":"Displaying OpenEdX connection status in Richie","description":"This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status"},"django-react-interop":{"id":"django-react-interop","title":"Connecting React components with Django","description":"richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.","sidebar":"docs"},"docker-development":{"id":"docker-development","title":"Developing Richie with Docker","description":"Now that you have Richie up and running, you can start working with it.","sidebar":"docs"},"filters-customization":{"id":"filters-customization","title":"Customizing search filters","description":"You may want to customize the filters on the left side bar of the search page.","sidebar":"docs"},"frontend-overrides":{"id":"frontend-overrides","title":"Overriding frontend components","description":"Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.","sidebar":"docs"},"installation":{"id":"installation","title":"Installing Richie for development","description":"Richie is a container-native application but can also be installed","sidebar":"docs"},"internationalization":{"id":"internationalization","title":"Internationalization","description":"richie has built-in localization and internationalization:","sidebar":"docs"},"joanie-connection":{"id":"joanie-connection","title":"Joanie Connection","description":"Joanie delivers an API able to manage course"},"lms-backends":{"id":"lms-backends","title":"Configuring LMS Backends","description":"Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless"},"lms-connection":{"id":"lms-connection","title":"Connecting Richie with one or more LMS","description":"Connecting Richie to an LMS","sidebar":"docs"},"native-installation":{"id":"native-installation","title":"Installing Richie on your machine","description":"This document aims to list all needed steps to have a working Richie","sidebar":"docs"},"synchronizing-course-runs":{"id":"synchronizing-course-runs","title":"Synchronizing course runs between Richie and OpenEdX","description":"Richie can receive automatic course runs updates on a dedicated API endpoint."},"tls-connection":{"id":"tls-connection","title":"Connecting Richie and OpenEdX over TLS for development","description":"Purpose"},"web-analytics":{"id":"web-analytics","title":"Add web analytics to your site","description":"Richie has native support to Google Universal Analytics and Google Tag Manager Web Analytics solutions.","sidebar":"docs"}}}')}}]); \ No newline at end of file diff --git a/assets/js/384badf6.dac3f2fb.js b/assets/js/384badf6.dac3f2fb.js new file mode 100644 index 0000000000..28bf486817 --- /dev/null +++ b/assets/js/384badf6.dac3f2fb.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[66496],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>m});var r=n(67294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var s=r.createContext({}),c=function(e){var t=r.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=c(e.components);return r.createElement(s.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),d=c(n),m=a,f=d["".concat(s,".").concat(m)]||d[m]||u[m]||o;return n?r.createElement(f,i(i({ref:t},p),{},{components:n})):r.createElement(f,i({ref:t},p))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=d;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l.mdxType="string"==typeof e?e:a,i[1]=l;for(var c=2;c{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>s,default:()=>m,frontMatter:()=>l,metadata:()=>c,toc:()=>u});var r=n(83117),a=n(80102),o=(n(67294),n(3905)),i=["components"],l={id:"discover",title:"Discover Richie",sidebar_label:"Discover Richie"},s=void 0,c={unversionedId:"discover",id:"version-2.22.0/discover",title:"Discover Richie",description:"Learning Management Systems (LMS) are great tools for hosting and playing interactive online",source:"@site/versioned_docs/version-2.22.0/discover.md",sourceDirName:".",slug:"/discover",permalink:"/docs/discover",draft:!1,tags:[],version:"2.22.0",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1682069684,formattedLastUpdatedAt:"Apr 21, 2023",frontMatter:{id:"discover",title:"Discover Richie",sidebar_label:"Discover Richie"},sidebar:"docs",next:{title:"Start your own site",permalink:"/docs/cookiecutter"}},p={},u=[{value:"Quick preview",id:"quick-preview",level:2},{value:"Start your own site",id:"start-your-own-site",level:2}],d={toc:u};function m(e){var t=e.components,l=(0,a.Z)(e,i);return(0,o.kt)("wrapper",(0,r.Z)({},d,l,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,(0,o.kt)("inlineCode",{parentName:"p"},"Learning Management Systems")," (LMS) are great tools for hosting and playing interactive online\ncourses and MOOCs."),(0,o.kt)("p",null,"However, if you need to build a complete website with flexible content to aggregate your courses,\nin several languages and from different sources, ",(0,o.kt)("strong",{parentName:"p"},"you will soon need a CMS"),"."),(0,o.kt)("p",null,'At "France Universit\xe9 Num\xe9rique", we wanted to build an OpenSource portal with ',(0,o.kt)("inlineCode",{parentName:"p"},"Python")," and\n",(0,o.kt)("inlineCode",{parentName:"p"},"Django"),". That's why we built ",(0,o.kt)("inlineCode",{parentName:"p"},"Richie")," on top of ",(0,o.kt)("a",{parentName:"p",href:"https://www.django-cms.org"},"DjangoCMS"),", one of\nthe best CMS on the market, as a toolbox to easily create full fledged websites with a catalog of\nonline courses."),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"screenshot of richie demo site",src:n(99872).Z,width:"2034",height:"1550"})),(0,o.kt)("p",null,"Among the features that ",(0,o.kt)("inlineCode",{parentName:"p"},"Richie")," offers out of the box:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"multi-lingual by default,"),(0,o.kt)("li",{parentName:"ul"},"advanced access rights and moderation,"),(0,o.kt)("li",{parentName:"ul"},"catalog of courses synchronized with one or more ",(0,o.kt)("inlineCode",{parentName:"li"},"LMS")," instances,"),(0,o.kt)("li",{parentName:"ul"},"search engine based on ",(0,o.kt)("inlineCode",{parentName:"li"},"Elasticsearch")," and pre-configured to offer full-text queries,\nmulti-facetting, auto-complete,..."),(0,o.kt)("li",{parentName:"ul"},"flexible custom pages for courses, organizations, categories, teachers, blog posts,\nprograms (and their inter-relations),"),(0,o.kt)("li",{parentName:"ul"},"Extensible with any third-party ",(0,o.kt)("inlineCode",{parentName:"li"},"DjangoCMS")," plugin or any third-party ",(0,o.kt)("inlineCode",{parentName:"li"},"Django")," application.")),(0,o.kt)("h2",{id:"quick-preview"},"Quick preview"),(0,o.kt)("p",null,"If you're looking for a quick preview of ",(0,o.kt)("inlineCode",{parentName:"p"},"Richie"),", you can take a look and have a tour of\n",(0,o.kt)("inlineCode",{parentName:"p"},"Richie")," on our dedicated ",(0,o.kt)("a",{parentName:"p",href:"https://demo.richie.education"},"demo site"),"."),(0,o.kt)("p",null,"It is connected back-to-back with a demo of OpenEdX running on\n",(0,o.kt)("a",{parentName:"p",href:"https://github.com/openfun/openedx-docker"},"OpenEdX Docker"),"."),(0,o.kt)("p",null,"Two users are available for testing:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"admin: ",(0,o.kt)("inlineCode",{parentName:"li"},"admin@example.com"),"/",(0,o.kt)("inlineCode",{parentName:"li"},"admin")),(0,o.kt)("li",{parentName:"ul"},"student: ",(0,o.kt)("inlineCode",{parentName:"li"},"edx@example.com"),"/",(0,o.kt)("inlineCode",{parentName:"li"},"edx"))),(0,o.kt)("p",null,"The database is regularly flushed."),(0,o.kt)("h2",{id:"start-your-own-site"},"Start your own site"),(0,o.kt)("p",null,"The next step after discovering Richie on the demo is to start your own project. We provide a\nproduction-ready template based on ",(0,o.kt)("a",{parentName:"p",href:"https://github.com/audreyr/cookiecutter"},"Cookiecutter")," that\nallows you to generate your project in seconds."),(0,o.kt)("p",null,"Follow the guide on ",(0,o.kt)("a",{parentName:"p",href:"/docs/cookiecutter"},"starting your own Richie site with Cookiecutter"),"."),(0,o.kt)("p",null,"Once you created a new site, you will be able to fully customize it:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"override any Django template or portion of template,"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("a",{parentName:"li",href:"/docs/frontend-overrides"},"override ReactJS components"),","),(0,o.kt)("li",{parentName:"ul"},"override some css rules or rebuild the whole css with your own variables and customizations,"),(0,o.kt)("li",{parentName:"ul"},"add any ",(0,o.kt)("a",{parentName:"li",href:"https://www.django-cms.org"},"DjangoCMS")," plugin or feature,"),(0,o.kt)("li",{parentName:"ul"},"add any ",(0,o.kt)("a",{parentName:"li",href:"https://djangopackages.org"},"Django third-party application"),".")))}m.isMDXComponent=!0},99872:(e,t,n)=>{n.d(t,{Z:()=>r});const r=n.p+"assets/images/demo-screenshot-53942dc373545f15ea8033f3f21f30a7.jpg"}}]); \ No newline at end of file diff --git a/assets/js/39917643.9709dbf5.js b/assets/js/39917643.9709dbf5.js new file mode 100644 index 0000000000..0b6dcf6d55 --- /dev/null +++ b/assets/js/39917643.9709dbf5.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[38737],{3905:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>m});var n=r(67294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function s(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function a(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var l=n.createContext({}),c=function(e){var t=n.useContext(l),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},u=function(e){var t=c(e.components);return n.createElement(l.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,s=e.originalType,l=e.parentName,u=i(e,["components","mdxType","originalType","parentName"]),d=c(r),m=o,f=d["".concat(l,".").concat(m)]||d[m]||p[m]||s;return r?n.createElement(f,a(a({ref:t},u),{},{components:r})):n.createElement(f,a({ref:t},u))}));function m(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var s=r.length,a=new Array(s);a[0]=d;var i={};for(var l in t)hasOwnProperty.call(t,l)&&(i[l]=t[l]);i.originalType=e,i.mdxType="string"==typeof e?e:o,a[1]=i;for(var c=2;c{r.r(t),r.d(t,{assets:()=>u,contentTitle:()=>l,default:()=>m,frontMatter:()=>i,metadata:()=>c,toc:()=>p});var n=r(83117),o=r(80102),s=(r(67294),r(3905)),a=["components"],i={id:"css-guidelines",title:"CSS Guidelines",sidebar_label:"CSS Guidelines"},l=void 0,c={unversionedId:"css-guidelines",id:"version-2.22.0/css-guidelines",title:"CSS Guidelines",description:"The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.",source:"@site/versioned_docs/version-2.22.0/css-guidelines.md",sourceDirName:".",slug:"/css-guidelines",permalink:"/docs/css-guidelines",draft:!1,tags:[],version:"2.22.0",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1682069684,formattedLastUpdatedAt:"Apr 21, 2023",frontMatter:{id:"css-guidelines",title:"CSS Guidelines",sidebar_label:"CSS Guidelines"},sidebar:"docs",previous:{title:"Accessibility testing",permalink:"/docs/accessibility-testing"}},u={},p=[{value:"File structuration",id:"file-structuration",level:2},{value:"Code structuration",id:"code-structuration",level:2},{value:"Quick pointers",id:"quick-pointers",level:2}],d={toc:p};function m(e){var t=e.components,r=(0,o.Z)(e,a);return(0,s.kt)("wrapper",(0,n.Z)({},d,r,{components:t,mdxType:"MDXLayout"}),(0,s.kt)("p",null,"The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly."),(0,s.kt)("p",null,"Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations."),(0,s.kt)("h2",{id:"file-structuration"},"File structuration"),(0,s.kt)("p",null,"Rules should be placed where their purpose is most apparent, and where they are easiest to find."),(0,s.kt)("p",null,"Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a ",(0,s.kt)("inlineCode",{parentName:"p"},".scss")," file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are ",(0,s.kt)("strong",{parentName:"p"},"specific to this component/template and this one only")),(0,s.kt)("p",null,"Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central ",(0,s.kt)("inlineCode",{parentName:"p"},"app/static/scss")," folder."),(0,s.kt)("h2",{id:"code-structuration"},"Code structuration"),(0,s.kt)("p",null,"In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes."),(0,s.kt)("p",null,"Following the ",(0,s.kt)("a",{parentName:"p",href:"http://getbem.com/introduction/"},"BEM naming convention"),", we will write our classes as follows :"),(0,s.kt)("pre",null,(0,s.kt)("code",{parentName:"pre"},".block {}\n.block__element {}\n.block--modifier {}\n")),(0,s.kt)("ul",null,(0,s.kt)("li",{parentName:"ul"},(0,s.kt)("inlineCode",{parentName:"li"},".block")," represents the higher level of an abstraction or component."),(0,s.kt)("li",{parentName:"ul"},(0,s.kt)("inlineCode",{parentName:"li"},".block__element")," represents a descendent of .block that helps form .block as a whole."),(0,s.kt)("li",{parentName:"ul"},(0,s.kt)("inlineCode",{parentName:"li"},".block--modifier")," represents a different state or version of .block.")),(0,s.kt)("p",null,"We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. ",(0,s.kt)("inlineCode",{parentName:"p"},".site-search__field--full"),")."),(0,s.kt)("p",null,"Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup."),(0,s.kt)("h2",{id:"quick-pointers"},"Quick pointers"),(0,s.kt)("ul",null,(0,s.kt)("li",{parentName:"ul"},"In general, ",(0,s.kt)("strong",{parentName:"li"},"do not use IDs"),". ",(0,s.kt)("em",{parentName:"li"},"They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.")),(0,s.kt)("li",{parentName:"ul"},"Do not nest selectors unnecessarily. ",(0,s.kt)("em",{parentName:"li"},"It will increase specificity and affect where else you can use your styles.")),(0,s.kt)("li",{parentName:"ul"},"Do not qualify selectors unnecessarily. ",(0,s.kt)("em",{parentName:"li"},"It will impact the number of different elements you can apply styles to.")),(0,s.kt)("li",{parentName:"ul"},"Comment profusely, ",(0,s.kt)("em",{parentName:"li"},"whenever you think the CSS is getting complex or it would not be easy to understand its intent.")),(0,s.kt)("li",{parentName:"ul"},"Use !important proactively. ",(0,s.kt)("em",{parentName:"li"},"!important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively."))),(0,s.kt)("p",null,"(Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. ",(0,s.kt)("inlineCode",{parentName:"p"},"div > #example")," is A LOT more efficient than ",(0,s.kt)("inlineCode",{parentName:"p"},"#example > div")," although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See ",(0,s.kt)("a",{parentName:"p",href:"http://csswizardry.com/2011/09/writing-efficient-css-selectors/"},"CSS Wizardry")," for more details."))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/3a6f03ad.7b6e5a8b.js b/assets/js/3a6f03ad.7b6e5a8b.js new file mode 100644 index 0000000000..e8239afa07 --- /dev/null +++ b/assets/js/3a6f03ad.7b6e5a8b.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[70473],{3905:(e,n,t)=>{t.d(n,{Zo:()=>u,kt:()=>m});var r=t(67294);function o(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function i(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);n&&(r=r.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,r)}return t}function a(e){for(var n=1;n=0||(o[t]=e[t]);return o}(e,n);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}var l=r.createContext({}),c=function(e){var n=r.useContext(l),t=n;return e&&(t="function"==typeof e?e(n):a(a({},n),e)),t},u=function(e){var n=c(e.components);return r.createElement(l.Provider,{value:n},e.children)},p={inlineCode:"code",wrapper:function(e){var n=e.children;return r.createElement(r.Fragment,{},n)}},d=r.forwardRef((function(e,n){var t=e.components,o=e.mdxType,i=e.originalType,l=e.parentName,u=s(e,["components","mdxType","originalType","parentName"]),d=c(t),m=o,h=d["".concat(l,".").concat(m)]||d[m]||p[m]||i;return t?r.createElement(h,a(a({ref:n},u),{},{components:t})):r.createElement(h,a({ref:n},u))}));function m(e,n){var t=arguments,o=n&&n.mdxType;if("string"==typeof e||o){var i=t.length,a=new Array(i);a[0]=d;var s={};for(var l in n)hasOwnProperty.call(n,l)&&(s[l]=n[l]);s.originalType=e,s.mdxType="string"==typeof e?e:o,a[1]=s;for(var c=2;c{t.r(n),t.d(n,{assets:()=>u,contentTitle:()=>l,default:()=>m,frontMatter:()=>s,metadata:()=>c,toc:()=>p});var r=t(83117),o=t(80102),i=(t(67294),t(3905)),a=["components"],s={id:"course-run-synchronization-api",title:"Course run synchronization API",sidebar_label:"course run sync"},l=void 0,c={unversionedId:"api/course-run-synchronization-api",id:"version-2.22.0/api/course-run-synchronization-api",title:"Course run synchronization API",description:"API endpoint allowing remote systems to synchronize their course runs with a Richie instance.",source:"@site/versioned_docs/version-2.22.0/api/course-run-synchronization-api.md",sourceDirName:"api",slug:"/api/course-run-synchronization-api",permalink:"/docs/api/course-run-synchronization-api",draft:!1,tags:[],version:"2.22.0",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1682069684,formattedLastUpdatedAt:"Apr 21, 2023",frontMatter:{id:"course-run-synchronization-api",title:"Course run synchronization API",sidebar_label:"course run sync"}},u={},p=[{value:"Synchronization endpoint /api/1.0/course-runs-sync",id:"synchronization-endpoint-api10course-runs-sync",level:2},{value:"Synchronize a course run POST",id:"synchronize-a-course-run-post",level:3}],d={toc:p};function m(e){var n=e.components,t=(0,o.Z)(e,a);return(0,i.kt)("wrapper",(0,r.Z)({},d,t,{components:n,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"API endpoint allowing remote systems to synchronize their course runs with a Richie instance."),(0,i.kt)("h2",{id:"synchronization-endpoint-api10course-runs-sync"},"Synchronization endpoint ","[/api/1.0/course-runs-sync]"),(0,i.kt)("p",null,'This documentation describes version "1.0" of this API endpoint.'),(0,i.kt)("h3",{id:"synchronize-a-course-run-post"},"Synchronize a course run ","[POST]"),(0,i.kt)("p",null,"It takes a JSON object containing the course run details:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"resource_link: ",(0,i.kt)("inlineCode",{parentName:"li"},"https://lms.example.com/courses/course-v1:001+001+001/info")," (string, required) -\nurl of the course syllabus on the LMS from which a unique course identifier can be extracted"),(0,i.kt)("li",{parentName:"ul"},"start: ",(0,i.kt)("inlineCode",{parentName:"li"},"2018-02-01T06:00:00Z")," (string, optional) - ISO 8601 date, when this session of the\ncourse starts"),(0,i.kt)("li",{parentName:"ul"},"end: ",(0,i.kt)("inlineCode",{parentName:"li"},"2018-02-28T06:00:00Z")," (string, optional) - ISO 8601 date, when this session of the course\nends"),(0,i.kt)("li",{parentName:"ul"},"enrollment_start: ",(0,i.kt)("inlineCode",{parentName:"li"},"2018-01-01T06:00:00Z")," (string, optional) - ISO 8601 date, when enrollment\nfor this session of the course starts"),(0,i.kt)("li",{parentName:"ul"},"enrollment_end: ",(0,i.kt)("inlineCode",{parentName:"li"},"2018-01-31T06:00:00Z")," (string, optional) - ISO 8601 date, when enrollment for\nthis session of the course ends"),(0,i.kt)("li",{parentName:"ul"},"languages: ","['fr', 'en']"," (array","[string]",", required) - ISO 639-1 code (2 letters) for the course's\nlanguages")),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"Request (application/json)"),(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"Headers"),(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"Authorization: ",(0,i.kt)("inlineCode",{parentName:"li"},"SIG-HMAC-SHA256 xxxxxxx")," (string, required) - Authorization header\ncontaining the digest of the utf-8 encoded json representation of the submitted data\nfor the given secret key and SHA256 digest algorithm (see ","[synchronizing-course-runs]","\nfor an example)."))),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"Body"),(0,i.kt)("pre",{parentName:"li"},(0,i.kt)("code",{parentName:"pre"},' {\n "resource_link": "https://lms.example.com/courses/course-v1:001+001+001/info",\n "start": "2021-02-01T00:00:00Z",\n "end": "2021-02-31T23:59:59Z",\n "enrollment_start": "2021-01-01T00:00:00Z",\n "enrollment_end": "2021-01-31T23:59:59Z",\n "languages": ["en", "fr"]\n }\n'))))),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"Response 200 (application/json)"),(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("p",{parentName:"li"},"Body"),(0,i.kt)("pre",{parentName:"li"},(0,i.kt)("code",{parentName:"pre"},' {\n "success": True\n }\n')))))))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/3c7d7aed.32b7a062.js b/assets/js/3c7d7aed.32b7a062.js new file mode 100644 index 0000000000..19ade48d6f --- /dev/null +++ b/assets/js/3c7d7aed.32b7a062.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[54688],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>m});var r=n(67294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function s(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=r.createContext({}),c=function(e){var t=r.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},p=function(e){var t=c(e.components);return r.createElement(l.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,a=e.originalType,l=e.parentName,p=o(e,["components","mdxType","originalType","parentName"]),d=c(n),m=i,h=d["".concat(l,".").concat(m)]||d[m]||u[m]||a;return n?r.createElement(h,s(s({ref:t},p),{},{components:n})):r.createElement(h,s({ref:t},p))}));function m(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=n.length,s=new Array(a);s[0]=d;var o={};for(var l in t)hasOwnProperty.call(t,l)&&(o[l]=t[l]);o.originalType=e,o.mdxType="string"==typeof e?e:i,s[1]=o;for(var c=2;c{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>l,default:()=>m,frontMatter:()=>o,metadata:()=>c,toc:()=>u});var r=n(83117),i=n(80102),a=(n(67294),n(3905)),s=["components"],o={id:"accessibility-testing",title:"Automated accessibility checks",sidebar_label:"Accessibility testing"},l=void 0,c={unversionedId:"accessibility-testing",id:"version-2.21.1/accessibility-testing",title:"Automated accessibility checks",description:"Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.",source:"@site/versioned_docs/version-2.21.1/accessibility-testing.md",sourceDirName:".",slug:"/accessibility-testing",permalink:"/docs/2.21.1/accessibility-testing",draft:!1,tags:[],version:"2.21.1",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1680624606,formattedLastUpdatedAt:"Apr 4, 2023",frontMatter:{id:"accessibility-testing",title:"Automated accessibility checks",sidebar_label:"Accessibility testing"},sidebar:"docs",previous:{title:"Contributing guide",permalink:"/docs/2.21.1/contributing-guide"},next:{title:"CSS Guidelines",permalink:"/docs/2.21.1/css-guidelines"}},p={},u=[{value:"Testing environment setup",id:"testing-environment-setup",level:2},{value:"Running the tests",id:"running-the-tests",level:2},{value:"Documentation reference",id:"documentation-reference",level:2}],d={toc:u};function m(e){var t=e.components,n=(0,i.Z)(e,s);return(0,a.kt)("wrapper",(0,r.Z)({},d,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"Richie includes automated accessibility checks built through a ",(0,a.kt)("inlineCode",{parentName:"p"},"Cypress")," end-to-end testing infrastructure."),(0,a.kt)("p",null,"Automated accessibility checks can only surface around 30% of possible problems on any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency."),(0,a.kt)("p",null,"We use ",(0,a.kt)("inlineCode",{parentName:"p"},"axe")," to run these checks. You can find more about axe on the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/dequelabs/axe-core"},(0,a.kt)("inlineCode",{parentName:"a"},"axe-core")," GitHub repository"),"."),(0,a.kt)("h2",{id:"testing-environment-setup"},"Testing environment setup"),(0,a.kt)("p",null,"Both ",(0,a.kt)("inlineCode",{parentName:"p"},"Cypress")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"axe")," are used through their respective NPM packages. This means everything goes through ",(0,a.kt)("inlineCode",{parentName:"p"},"yarn")," commands. You need to have ",(0,a.kt)("inlineCode",{parentName:"p"},"node")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"yarn")," installed locally to run the tests."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"cd tests_e2e\nyarn install\n")),(0,a.kt)("p",null,"This should install everything you need."),(0,a.kt)("h2",{id:"running-the-tests"},"Running the tests"),(0,a.kt)("p",null,"There are two ways to use the ",(0,a.kt)("inlineCode",{parentName:"p"},"Cypress")," tests: through a terminal-based runner and through the ",(0,a.kt)("inlineCode",{parentName:"p"},"Cypress")," UI. Both are started through ",(0,a.kt)("inlineCode",{parentName:"p"},"yarn")," but they have different use cases."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"yarn cypress run\n")),(0,a.kt)("p",null,"You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running ",(0,a.kt)("inlineCode",{parentName:"p"},"Cypress")," in the CI."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"yarn cypress open\n")),(0,a.kt)("p",null,"This command simply opens the ",(0,a.kt)("inlineCode",{parentName:"p"},"Cypress")," UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations."),(0,a.kt)("p",null,"When there are a11y violations, an assertion fails and prints out a list in the ",(0,a.kt)("inlineCode",{parentName:"p"},"Cypress")," UI. You can then click on a violation to print more information in the browser console."),(0,a.kt)("h2",{id:"documentation-reference"},"Documentation reference"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://dequeuniversity.com/rules/axe/3.4"},"List of all possible violations covered by ",(0,a.kt)("inlineCode",{parentName:"a"},"axe"))),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://docs.cypress.io"},(0,a.kt)("inlineCode",{parentName:"a"},"Cypress")," documentation")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/avanslaars/cypress-axe"},(0,a.kt)("inlineCode",{parentName:"a"},"axe")," and ",(0,a.kt)("inlineCode",{parentName:"a"},"Cypress")," integration"))))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/3c7d7aed.9a20e8f0.js b/assets/js/3c7d7aed.9a20e8f0.js deleted file mode 100644 index 7fa540baa3..0000000000 --- a/assets/js/3c7d7aed.9a20e8f0.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[54688],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>m});var r=n(67294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function s(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var l=r.createContext({}),c=function(e){var t=r.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},p=function(e){var t=c(e.components);return r.createElement(l.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,i=e.mdxType,a=e.originalType,l=e.parentName,p=o(e,["components","mdxType","originalType","parentName"]),d=c(n),m=i,h=d["".concat(l,".").concat(m)]||d[m]||u[m]||a;return n?r.createElement(h,s(s({ref:t},p),{},{components:n})):r.createElement(h,s({ref:t},p))}));function m(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var a=n.length,s=new Array(a);s[0]=d;var o={};for(var l in t)hasOwnProperty.call(t,l)&&(o[l]=t[l]);o.originalType=e,o.mdxType="string"==typeof e?e:i,s[1]=o;for(var c=2;c{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>l,default:()=>m,frontMatter:()=>o,metadata:()=>c,toc:()=>u});var r=n(83117),i=n(80102),a=(n(67294),n(3905)),s=["components"],o={id:"accessibility-testing",title:"Automated accessibility checks",sidebar_label:"Accessibility testing"},l=void 0,c={unversionedId:"accessibility-testing",id:"version-2.21.1/accessibility-testing",title:"Automated accessibility checks",description:"Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.",source:"@site/versioned_docs/version-2.21.1/accessibility-testing.md",sourceDirName:".",slug:"/accessibility-testing",permalink:"/docs/accessibility-testing",draft:!1,tags:[],version:"2.21.1",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1680624606,formattedLastUpdatedAt:"Apr 4, 2023",frontMatter:{id:"accessibility-testing",title:"Automated accessibility checks",sidebar_label:"Accessibility testing"},sidebar:"docs",previous:{title:"Contributing guide",permalink:"/docs/contributing-guide"},next:{title:"CSS Guidelines",permalink:"/docs/css-guidelines"}},p={},u=[{value:"Testing environment setup",id:"testing-environment-setup",level:2},{value:"Running the tests",id:"running-the-tests",level:2},{value:"Documentation reference",id:"documentation-reference",level:2}],d={toc:u};function m(e){var t=e.components,n=(0,i.Z)(e,s);return(0,a.kt)("wrapper",(0,r.Z)({},d,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"Richie includes automated accessibility checks built through a ",(0,a.kt)("inlineCode",{parentName:"p"},"Cypress")," end-to-end testing infrastructure."),(0,a.kt)("p",null,"Automated accessibility checks can only surface around 30% of possible problems on any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency."),(0,a.kt)("p",null,"We use ",(0,a.kt)("inlineCode",{parentName:"p"},"axe")," to run these checks. You can find more about axe on the ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/dequelabs/axe-core"},(0,a.kt)("inlineCode",{parentName:"a"},"axe-core")," GitHub repository"),"."),(0,a.kt)("h2",{id:"testing-environment-setup"},"Testing environment setup"),(0,a.kt)("p",null,"Both ",(0,a.kt)("inlineCode",{parentName:"p"},"Cypress")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"axe")," are used through their respective NPM packages. This means everything goes through ",(0,a.kt)("inlineCode",{parentName:"p"},"yarn")," commands. You need to have ",(0,a.kt)("inlineCode",{parentName:"p"},"node")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"yarn")," installed locally to run the tests."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"cd tests_e2e\nyarn install\n")),(0,a.kt)("p",null,"This should install everything you need."),(0,a.kt)("h2",{id:"running-the-tests"},"Running the tests"),(0,a.kt)("p",null,"There are two ways to use the ",(0,a.kt)("inlineCode",{parentName:"p"},"Cypress")," tests: through a terminal-based runner and through the ",(0,a.kt)("inlineCode",{parentName:"p"},"Cypress")," UI. Both are started through ",(0,a.kt)("inlineCode",{parentName:"p"},"yarn")," but they have different use cases."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"yarn cypress run\n")),(0,a.kt)("p",null,"You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running ",(0,a.kt)("inlineCode",{parentName:"p"},"Cypress")," in the CI."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"yarn cypress open\n")),(0,a.kt)("p",null,"This command simply opens the ",(0,a.kt)("inlineCode",{parentName:"p"},"Cypress")," UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations."),(0,a.kt)("p",null,"When there are a11y violations, an assertion fails and prints out a list in the ",(0,a.kt)("inlineCode",{parentName:"p"},"Cypress")," UI. You can then click on a violation to print more information in the browser console."),(0,a.kt)("h2",{id:"documentation-reference"},"Documentation reference"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://dequeuniversity.com/rules/axe/3.4"},"List of all possible violations covered by ",(0,a.kt)("inlineCode",{parentName:"a"},"axe"))),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://docs.cypress.io"},(0,a.kt)("inlineCode",{parentName:"a"},"Cypress")," documentation")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/avanslaars/cypress-axe"},(0,a.kt)("inlineCode",{parentName:"a"},"axe")," and ",(0,a.kt)("inlineCode",{parentName:"a"},"Cypress")," integration"))))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/3e42226b.6fbd735c.js b/assets/js/3e42226b.6fbd735c.js new file mode 100644 index 0000000000..fcaefa20c0 --- /dev/null +++ b/assets/js/3e42226b.6fbd735c.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[22157],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>h});var a=n(67294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function l(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var s=a.createContext({}),p=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},u=function(e){var t=p(e.components);return a.createElement(s.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},c=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,l=e.originalType,s=e.parentName,u=o(e,["components","mdxType","originalType","parentName"]),c=p(n),h=r,m=c["".concat(s,".").concat(h)]||c[h]||d[h]||l;return n?a.createElement(m,i(i({ref:t},u),{},{components:n})):a.createElement(m,i({ref:t},u))}));function h(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var l=n.length,i=new Array(l);i[0]=c;var o={};for(var s in t)hasOwnProperty.call(t,s)&&(o[s]=t[s]);o.originalType=e,o.mdxType="string"==typeof e?e:r,i[1]=o;for(var p=2;p{n.r(t),n.d(t,{assets:()=>u,contentTitle:()=>s,default:()=>h,frontMatter:()=>o,metadata:()=>p,toc:()=>d});var a=n(83117),r=n(80102),l=(n(67294),n(3905)),i=["components"],o={id:"native-installation",title:"Installing Richie on your machine",sidebar_label:"Native installation"},s=void 0,p={unversionedId:"native-installation",id:"version-2.22.0/native-installation",title:"Installing Richie on your machine",description:"This document aims to list all needed steps to have a working Richie",source:"@site/versioned_docs/version-2.22.0/native-installation.md",sourceDirName:".",slug:"/native-installation",permalink:"/docs/native-installation",draft:!1,tags:[],version:"2.22.0",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1682069684,formattedLastUpdatedAt:"Apr 21, 2023",frontMatter:{id:"native-installation",title:"Installing Richie on your machine",sidebar_label:"Native installation"},sidebar:"docs",previous:{title:"Docker development",permalink:"/docs/docker-development"},next:{title:"Contributing guide",permalink:"/docs/contributing-guide"}},u={},d=[{value:"Installing a fresh server",id:"installing-a-fresh-server",level:2},{value:"Version",id:"version",level:3},{value:"System update",id:"system-update",level:3},{value:"Database part",id:"database-part",level:2},{value:"Elasticsearch",id:"elasticsearch",level:2},{value:"Ubuntu",id:"ubuntu",level:3},{value:"OS X",id:"os-x",level:3},{value:"Application part",id:"application-part",level:2},{value:"Python and other requirements",id:"python-and-other-requirements",level:3},{value:"The virtualenv",id:"the-virtualenv",level:3},{value:"Frontend build",id:"frontend-build",level:3},{value:"Run server",id:"run-server",level:3}],c={toc:d};function h(e){var t=e.components,n=(0,r.Z)(e,i);return(0,l.kt)("wrapper",(0,a.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,l.kt)("p",null,"This document aims to list all needed steps to have a working ",(0,l.kt)("inlineCode",{parentName:"p"},"Richie"),"\ninstallation on your laptop."),(0,l.kt)("p",null,"A better approach is to use ",(0,l.kt)("a",{parentName:"p",href:"https://docs.docker.com"},(0,l.kt)("inlineCode",{parentName:"a"},"Docker"))," as explained in\nour guide for ",(0,l.kt)("a",{parentName:"p",href:"/docs/installation"},"container-native installation")," instructions."),(0,l.kt)("h2",{id:"installing-a-fresh-server"},"Installing a fresh server"),(0,l.kt)("h3",{id:"version"},"Version"),(0,l.kt)("p",null,"You need a ",(0,l.kt)("inlineCode",{parentName:"p"},"Ubuntu 18.04 Bionic Beaver")," (the latest LTS version) fresh\ninstallation."),(0,l.kt)("p",null,"If you are using another operating system or distribution, you can use\n",(0,l.kt)("a",{parentName:"p",href:"https://docs.vagrantup.com/v2/getting-started/index.html"},(0,l.kt)("inlineCode",{parentName:"a"},"Vagrant"))," to get a\nrunning Ubuntu 18.04 server in seconds."),(0,l.kt)("h3",{id:"system-update"},"System update"),(0,l.kt)("p",null,"Be sure to have fresh packages on the server (kernel, libc, ssl patches...):\npost"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-sh"},"sudo apt-get -y update\nsudo apt-get -y dist-upgrade\n")),(0,l.kt)("h2",{id:"database-part"},"Database part"),(0,l.kt)("p",null,"You must first install ",(0,l.kt)("inlineCode",{parentName:"p"},"postgresql"),"."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-sh"},"// On Linux\nsudo apt-get -y install postgresql\n\n// On OS X\nbrew install postgresql@10\nbrew services start postgresql@10\n// don't forget to add your new postgres install to the $PATH\n")),(0,l.kt)("p",null,(0,l.kt)("inlineCode",{parentName:"p"},"Postgresql")," is now running."),(0,l.kt)("p",null,"Then you can create the database owner and the database itself, using the\n",(0,l.kt)("inlineCode",{parentName:"p"},"postgres")," user:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-sh"},"sudo -u postgres -i // skip this on OS X as the default install will use your local user\ncreateuser fun -sP\n")),(0,l.kt)("p",null,"Note: we created the user as a superuser. This should only be done in dev/test\nenvironments."),(0,l.kt)("p",null,"Now, create the database with this user:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-sh"},"createdb richie -O fun -W\nexit\n")),(0,l.kt)("h2",{id:"elasticsearch"},"Elasticsearch"),(0,l.kt)("h3",{id:"ubuntu"},"Ubuntu"),(0,l.kt)("p",null,"Download and install the Public Signing Key"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"$ wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -\n")),(0,l.kt)("p",null,"You may need to install the apt-transport-https package on Debian before\nproceeding:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"$ sudo apt-get install apt-transport-https\n")),(0,l.kt)("p",null,"Save the repository definition to /etc/apt/sources.list.d/elastic-6.3.1.list:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},'$ echo "deb https://artifacts.elastic.co/packages/6.3.1/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-6.3.1.list\n')),(0,l.kt)("p",null,"Update repository and install"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"$ sudo apt-get update\n$ sudo apt-get install elasticsearch\n$ sudo /etc/init.d/elasticsearch start\n")),(0,l.kt)("h3",{id:"os-x"},"OS X"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"$ brew install elasticsearch\n")),(0,l.kt)("h2",{id:"application-part"},"Application part"),(0,l.kt)("h3",{id:"python-and-other-requirements"},"Python and other requirements"),(0,l.kt)("p",null,"We use ",(0,l.kt)("inlineCode",{parentName:"p"},"Python 3.6")," which is the one installed by default in ",(0,l.kt)("inlineCode",{parentName:"p"},"Ubuntu 18.04"),"."),(0,l.kt)("p",null,"You can install it on OS X using the following commands. Make sure to always run\n",(0,l.kt)("inlineCode",{parentName:"p"},"python3")," instead of ",(0,l.kt)("inlineCode",{parentName:"p"},"python")," and ",(0,l.kt)("inlineCode",{parentName:"p"},"pip3")," instead of ",(0,l.kt)("inlineCode",{parentName:"p"},"pip")," to ensure the correct\nversion of Python (your homebrew install of 3) is used."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"brew install python3\nbrew postinstall python3\n")),(0,l.kt)("h3",{id:"the-virtualenv"},"The virtualenv"),(0,l.kt)("p",null,"Place yourself in the application directory ",(0,l.kt)("inlineCode",{parentName:"p"},"app"),":"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"cd app\n")),(0,l.kt)("p",null,"We choose to run our application in a virtual environment."),(0,l.kt)("p",null,"For this, we'll install ",(0,l.kt)("inlineCode",{parentName:"p"},"virtualenvwrapper")," and add an environment:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"pip install virtualenvwrapper\n")),(0,l.kt)("p",null,"You can open a new shell to activate the virtualenvwrapper commands, or simply\ndo:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"source $(which virtualenvwrapper.sh)\n")),(0,l.kt)("p",null,"Then create the virtual environment for ",(0,l.kt)("inlineCode",{parentName:"p"},"richie"),":"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"mkvirtualenv richie --no-site-packages --python=python3\n")),(0,l.kt)("p",null,"The virtualenv should now be activated and you can install the Python\ndependencies for development:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"pip install -e .[dev]\n")),(0,l.kt)("p",null,'The "dev.txt" requirement file installs packages specific to a dev environment\nand should not be used in production.'),(0,l.kt)("h3",{id:"frontend-build"},"Frontend build"),(0,l.kt)("p",null,"This project is a hybrid that uses both Django generated pages and frontend JS\ncode. As such, it includes a frontend build process that comes in two parts: JS\n& CSS."),(0,l.kt)("p",null,"We need NPM to install the dependencies and run the build, which depends on a\nversion of Nodejs specified in ",(0,l.kt)("inlineCode",{parentName:"p"},".nvmrc"),". See ",(0,l.kt)("a",{parentName:"p",href:"https://github.com/creationix/nvm"},"the\nrepo")," for instructions on how to install NVM.\nTo take advantage of ",(0,l.kt)("inlineCode",{parentName:"p"},".nvmrc"),", run this in the context of the repository:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"nvm install\nnvm use\n")),(0,l.kt)("p",null,"As a prerequisite to running the frontend build for either JS or CSS, you'll\nneed to ",(0,l.kt)("a",{parentName:"p",href:"https://yarnpkg.com/lang/en/docs/install/"},"install yarn")," and download\ndependencies ",(0,l.kt)("em",{parentName:"p"},"via"),":"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"yarn install\n")),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},"JS build")),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-bash"},"npm run build\n")),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},"CSS build")),(0,l.kt)("p",null,"This will compile all our SCSS files into one bundle and put it in the static\nfolder we're serving."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"npm run sass\n")),(0,l.kt)("h3",{id:"run-server"},"Run server"),(0,l.kt)("p",null,"Make sure your database is up-to-date before running the application the first\ntime and after each modification to your models:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"python sandbox/manage.py migrate\n")),(0,l.kt)("p",null,"You can create a superuser account:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"python sandbox/manage.py createsuperuser\n")),(0,l.kt)("p",null,"Run the tests"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"python sandbox/manage.py test\n")),(0,l.kt)("p",null,"You should now be able to start Django and view the site at\n",(0,l.kt)("a",{parentName:"p",href:"http://localhost:8000"},"localhost:8000")),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},"python sandbox/manage.py runserver\n")))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/4220d24c.30598178.js b/assets/js/4220d24c.30598178.js new file mode 100644 index 0000000000..edacad5740 --- /dev/null +++ b/assets/js/4220d24c.30598178.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[4464],{3905:(e,n,t)=>{t.d(n,{Zo:()=>c,kt:()=>h});var a=t(67294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function o(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var s=a.createContext({}),u=function(e){var n=a.useContext(s),t=n;return e&&(t="function"==typeof e?e(n):o(o({},n),e)),t},c=function(e){var n=u(e.components);return a.createElement(s.Provider,{value:n},e.children)},p={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},d=a.forwardRef((function(e,n){var t=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),d=u(t),h=i,m=d["".concat(s,".").concat(h)]||d[h]||p[h]||r;return t?a.createElement(m,o(o({ref:n},c),{},{components:t})):a.createElement(m,o({ref:n},c))}));function h(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var r=t.length,o=new Array(r);o[0]=d;var l={};for(var s in n)hasOwnProperty.call(n,s)&&(l[s]=n[s]);l.originalType=e,l.mdxType="string"==typeof e?e:i,o[1]=l;for(var u=2;u{t.r(n),t.d(n,{assets:()=>c,contentTitle:()=>s,default:()=>h,frontMatter:()=>l,metadata:()=>u,toc:()=>p});var a=t(83117),i=t(80102),r=(t(67294),t(3905)),o=["components"],l={id:"lms-backends",title:"Configuring LMS Backends",sidebar_label:"LMS Backends"},s=void 0,u={unversionedId:"lms-backends",id:"version-2.21.1/lms-backends",title:"Configuring LMS Backends",description:"Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless",source:"@site/versioned_docs/version-2.21.1/lms-backends.md",sourceDirName:".",slug:"/lms-backends",permalink:"/docs/2.21.1/lms-backends",draft:!1,tags:[],version:"2.21.1",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1680624606,formattedLastUpdatedAt:"Apr 4, 2023",frontMatter:{id:"lms-backends",title:"Configuring LMS Backends",sidebar_label:"LMS Backends"}},c={},p=[{value:"Prerequisites",id:"prerequisites",level:2},{value:"Configuring the LMS handler",id:"configuring-the-lms-handler",level:2},{value:"BASE_URL",id:"base_url",level:3},{value:"BACKEND",id:"backend",level:3},{value:"COURSE_REGEX",id:"course_regex",level:3},{value:"JS_BACKEND",id:"js_backend",level:3},{value:"JS_COURSE_REGEX",id:"js_course_regex",level:3},{value:"DEFAULT_COURSE_RUN_SYNC_MODE",id:"default_course_run_sync_mode",level:3},{value:"COURSE_RUN_SYNC_NO_UPDATE_FIELDS",id:"course_run_sync_no_update_fields",level:3},{value:"Technical support",id:"technical-support",level:2}],d={toc:p};function h(e){var n=e.components,t=(0,i.Z)(e,o);return(0,r.kt)("wrapper",(0,a.Z)({},d,t,{components:n,mdxType:"MDXLayout"}),(0,r.kt)("p",null,"Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless\nexperience between browsing the course catalog on Richie, enrolling to a course and following the\ncourse itself on the LMS."),(0,r.kt)("p",null,"It is possible to do the same with Moodle or any other LMS exposing an enrollment API, at the\ncost of writing a custom LMS handler backend."),(0,r.kt)("h2",{id:"prerequisites"},"Prerequisites"),(0,r.kt)("p",null,"This connection requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that\nare both subdomains of the same root domain, e.g. ",(0,r.kt)("inlineCode",{parentName:"p"},"richie.example.com")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"lms.example.com"),"."),(0,r.kt)("p",null,"OpenEdX will need to generate a CORS CSRF Cookie and this cookie is flagged as secure, which\nimplies that we are not able to use it without SSL connections."),(0,r.kt)("p",null,"As a consequence, you need to configure your OpenEdX instance as follows:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-python"},'FEATURES = {\n ...\n "ENABLE_CORS_HEADERS": True,\n "ENABLE_CROSS_DOMAIN_CSRF_COOKIE": True,\n}\n\nCORS_ALLOW_CREDENTIALS = True\nCORS_ALLOW_INSECURE = False\nCORS_ORIGIN_WHITELIST: ["richie.example.com"] # The domain on which Richie is hosted\n\nCROSS_DOMAIN_CSRF_COOKIE_DOMAIN = ".example.com" # The parent domain shared by Richie and OpenEdX\nCROSS_DOMAIN_CSRF_COOKIE_NAME: "edx_csrf_token"\nSESSION_COOKIE_NAME: "edx_session"\n')),(0,r.kt)("h2",{id:"configuring-the-lms-handler"},"Configuring the LMS handler"),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"LMSHandler")," utility acts as a proxy that routes queries to the correct LMS backend API,\nbased on a regex match on the URL of the course. It is configured via the ",(0,r.kt)("inlineCode",{parentName:"p"},"RICHIE_LMS_BACKENDS"),"\nsetting. As an example, here is how it would be configured to connect to an Ironwood OpenEdX\ninstance hosted on ",(0,r.kt)("inlineCode",{parentName:"p"},"https://lms.example.com"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-python"},'RICHIE_LMS_BACKENDS=[\n {\n "BASE_URL": "https://lms.example.com",\n # Django\n "BACKEND": "richie.apps.courses.lms.edx.EdXLMSBackend",\n "COURSE_REGEX": r"^https://lms\\.example\\.com/courses/(?P.*)/course/?$",\n # ReactJS\n "JS_BACKEND": "openedx-hawthorn",\n "JS_COURSE_REGEX": r"^https://lms\\.example\\.com/courses/(.*)/course/?$",\n # Course runs synchronization\n "COURSE_RUN_SYNC_NO_UPDATE_FIELDS": [],\n "DEFAULT_COURSE_RUN_SYNC_MODE": "sync_to_public",\n },\n]\n')),(0,r.kt)("p",null,"The following should help you understand how to configure this setting:"),(0,r.kt)("h3",{id:"base_url"},"BASE_URL"),(0,r.kt)("p",null,"The base url on which the OpenEdX instance is hosted. This is used to construct the complete url\nof the API endpoint on which the enrollment request is made by Richie's frontend application."),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Type: string"),(0,r.kt)("li",{parentName:"ul"},"Required: Yes"),(0,r.kt)("li",{parentName:"ul"},"Value: for example ",(0,r.kt)("a",{parentName:"li",href:"https://lms.example.com"},"https://lms.example.com"))),(0,r.kt)("h3",{id:"backend"},"BACKEND"),(0,r.kt)("p",null,"The path to a Python class serving as LMS backend for the targeted LMS."),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Type: string"),(0,r.kt)("li",{parentName:"ul"},"Required: Yes"),(0,r.kt)("li",{parentName:"ul"},"Value: Richie ships with the following Python backends (custom backends can be written to fit\nanother specific LMS):",(0,r.kt)("ul",{parentName:"li"},(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"richie.apps.courses.lms.edx.EdXLMSBackend"),": backend for OpenEdX"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"richie.apps.courses.lms.base.BaseLMSBackend"),": fake backend for development purposes")))),(0,r.kt)("h3",{id:"course_regex"},"COURSE_REGEX"),(0,r.kt)("p",null,"A Python regex that should match the course syllabus urls of the targeted LMS and return a\n",(0,r.kt)("inlineCode",{parentName:"p"},"course_id")," named group on the id of the course extracted from these urls."),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Type: string"),(0,r.kt)("li",{parentName:"ul"},"Required: Yes"),(0,r.kt)("li",{parentName:"ul"},"Value: for example ",(0,r.kt)("inlineCode",{parentName:"li"},"^.*/courses/(?P.*)/course/?$"))),(0,r.kt)("h3",{id:"js_backend"},"JS_BACKEND"),(0,r.kt)("p",null,"The name of the ReactJS backend to use for the targeted LMS."),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Type: string"),(0,r.kt)("li",{parentName:"ul"},"Required: Yes"),(0,r.kt)("li",{parentName:"ul"},"Value: Richie ships with the following Javascript backends (custom backends can be written to\nfit another specific LMS):",(0,r.kt)("ul",{parentName:"li"},(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"openedx-dogwood"),": backend for OpenEdX versions equal to ",(0,r.kt)("inlineCode",{parentName:"li"},"dogwood")," or ",(0,r.kt)("inlineCode",{parentName:"li"},"eucalyptus")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"openedx-hawthorn"),": backend for OpenEdX versions equal to ",(0,r.kt)("inlineCode",{parentName:"li"},"hawthorn")," or higher"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"openedx-fonzie"),": backend for OpenEdX via ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/openfun/fonzie"},"Fonzie"),"\n(extra user info and JWT tokens)"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"dummy"),": fake backend for development purposes")))),(0,r.kt)("h3",{id:"js_course_regex"},"JS_COURSE_REGEX"),(0,r.kt)("p",null,"A Javascript regex that should match the course syllabus urls of the targeted LMS and return an\nunnamed group on the id of the course extracted from these urls."),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Type: string"),(0,r.kt)("li",{parentName:"ul"},"Required: Yes"),(0,r.kt)("li",{parentName:"ul"},"Value: for example ",(0,r.kt)("inlineCode",{parentName:"li"},"^.*/courses/(.*)/course/?$"))),(0,r.kt)("h3",{id:"default_course_run_sync_mode"},"DEFAULT_COURSE_RUN_SYNC_MODE"),(0,r.kt)("p",null,"When a course run is created, this setting is used to set the value of the ",(0,r.kt)("inlineCode",{parentName:"p"},"sync_mode")," field.\nThis value defines how the course runs synchronization script will impact this course run after\ncreation."),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Type: enum(string)"),(0,r.kt)("li",{parentName:"ul"},"Required: No"),(0,r.kt)("li",{parentName:"ul"},"Value: possible values are ",(0,r.kt)("inlineCode",{parentName:"li"},"manual"),", ",(0,r.kt)("inlineCode",{parentName:"li"},"sync_to_draft")," and ",(0,r.kt)("inlineCode",{parentName:"li"},"sync_to_public"),(0,r.kt)("ul",{parentName:"li"},(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"manual"),": this course run is ignored by the course runs synchronization script"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"sync_to_draft"),": only the draft version of this course run is synchronized. A manual\npublication is necessary for the update to be visible on the public site."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"sync_to_public"),": the public version of this course run is updated by the synchronization\nscript. As a results, updates are directly visible on the public site without further\npublication by a staff user in Richie.")))),(0,r.kt)("h3",{id:"course_run_sync_no_update_fields"},"COURSE_RUN_SYNC_NO_UPDATE_FIELDS"),(0,r.kt)("p",null,"A list of fields that must only be set the first time a course run is synchronized. During this\nfirst synchronization, a new course run is created in Richie and all fields sent to the API\nendpoint via the payload are set on the object. For subsequent synchronization calls, the fields\nlisted in this setting are ignored and not synchronized. This can be used to allow modifying some\nfields manually in Richie regardless of what OpenEdX sends after an initial value is set."),(0,r.kt)("p",null,"Note that this setting is only effective for course runs with the ",(0,r.kt)("inlineCode",{parentName:"p"},"sync_mode")," field set to a\nvalue other then ",(0,r.kt)("inlineCode",{parentName:"p"},"manual"),"."),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Type: enum(string)"),(0,r.kt)("li",{parentName:"ul"},"Required: No"),(0,r.kt)("li",{parentName:"ul"},"Value: for example ",'["languages"]')),(0,r.kt)("h2",{id:"technical-support"},"Technical support"),(0,r.kt)("p",null,"If you encounter an issue with this documentation or the backends included in Richie, please\n",(0,r.kt)("a",{parentName:"p",href:"https://github.com/openfun/richie/issues"},"open an issue on our repository"),"."),(0,r.kt)("p",null,"If you need a custom backend, you can ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/openfun/richie/pulls"},"submit a PR")," or\n",(0,r.kt)("a",{parentName:"p",href:"https://github.com/openfun/richie/issues"},"open an issue")," and we will consider adding it."))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/4220d24c.e7b32db4.js b/assets/js/4220d24c.e7b32db4.js deleted file mode 100644 index cacc3b64a4..0000000000 --- a/assets/js/4220d24c.e7b32db4.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[4464],{3905:(e,n,t)=>{t.d(n,{Zo:()=>c,kt:()=>h});var a=t(67294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);n&&(a=a.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,a)}return t}function o(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var s=a.createContext({}),u=function(e){var n=a.useContext(s),t=n;return e&&(t="function"==typeof e?e(n):o(o({},n),e)),t},c=function(e){var n=u(e.components);return a.createElement(s.Provider,{value:n},e.children)},p={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},d=a.forwardRef((function(e,n){var t=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),d=u(t),h=i,m=d["".concat(s,".").concat(h)]||d[h]||p[h]||r;return t?a.createElement(m,o(o({ref:n},c),{},{components:t})):a.createElement(m,o({ref:n},c))}));function h(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var r=t.length,o=new Array(r);o[0]=d;var l={};for(var s in n)hasOwnProperty.call(n,s)&&(l[s]=n[s]);l.originalType=e,l.mdxType="string"==typeof e?e:i,o[1]=l;for(var u=2;u{t.r(n),t.d(n,{assets:()=>c,contentTitle:()=>s,default:()=>h,frontMatter:()=>l,metadata:()=>u,toc:()=>p});var a=t(83117),i=t(80102),r=(t(67294),t(3905)),o=["components"],l={id:"lms-backends",title:"Configuring LMS Backends",sidebar_label:"LMS Backends"},s=void 0,u={unversionedId:"lms-backends",id:"version-2.21.1/lms-backends",title:"Configuring LMS Backends",description:"Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless",source:"@site/versioned_docs/version-2.21.1/lms-backends.md",sourceDirName:".",slug:"/lms-backends",permalink:"/docs/lms-backends",draft:!1,tags:[],version:"2.21.1",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1680624606,formattedLastUpdatedAt:"Apr 4, 2023",frontMatter:{id:"lms-backends",title:"Configuring LMS Backends",sidebar_label:"LMS Backends"}},c={},p=[{value:"Prerequisites",id:"prerequisites",level:2},{value:"Configuring the LMS handler",id:"configuring-the-lms-handler",level:2},{value:"BASE_URL",id:"base_url",level:3},{value:"BACKEND",id:"backend",level:3},{value:"COURSE_REGEX",id:"course_regex",level:3},{value:"JS_BACKEND",id:"js_backend",level:3},{value:"JS_COURSE_REGEX",id:"js_course_regex",level:3},{value:"DEFAULT_COURSE_RUN_SYNC_MODE",id:"default_course_run_sync_mode",level:3},{value:"COURSE_RUN_SYNC_NO_UPDATE_FIELDS",id:"course_run_sync_no_update_fields",level:3},{value:"Technical support",id:"technical-support",level:2}],d={toc:p};function h(e){var n=e.components,t=(0,i.Z)(e,o);return(0,r.kt)("wrapper",(0,a.Z)({},d,t,{components:n,mdxType:"MDXLayout"}),(0,r.kt)("p",null,"Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless\nexperience between browsing the course catalog on Richie, enrolling to a course and following the\ncourse itself on the LMS."),(0,r.kt)("p",null,"It is possible to do the same with Moodle or any other LMS exposing an enrollment API, at the\ncost of writing a custom LMS handler backend."),(0,r.kt)("h2",{id:"prerequisites"},"Prerequisites"),(0,r.kt)("p",null,"This connection requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that\nare both subdomains of the same root domain, e.g. ",(0,r.kt)("inlineCode",{parentName:"p"},"richie.example.com")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"lms.example.com"),"."),(0,r.kt)("p",null,"OpenEdX will need to generate a CORS CSRF Cookie and this cookie is flagged as secure, which\nimplies that we are not able to use it without SSL connections."),(0,r.kt)("p",null,"As a consequence, you need to configure your OpenEdX instance as follows:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-python"},'FEATURES = {\n ...\n "ENABLE_CORS_HEADERS": True,\n "ENABLE_CROSS_DOMAIN_CSRF_COOKIE": True,\n}\n\nCORS_ALLOW_CREDENTIALS = True\nCORS_ALLOW_INSECURE = False\nCORS_ORIGIN_WHITELIST: ["richie.example.com"] # The domain on which Richie is hosted\n\nCROSS_DOMAIN_CSRF_COOKIE_DOMAIN = ".example.com" # The parent domain shared by Richie and OpenEdX\nCROSS_DOMAIN_CSRF_COOKIE_NAME: "edx_csrf_token"\nSESSION_COOKIE_NAME: "edx_session"\n')),(0,r.kt)("h2",{id:"configuring-the-lms-handler"},"Configuring the LMS handler"),(0,r.kt)("p",null,"The ",(0,r.kt)("inlineCode",{parentName:"p"},"LMSHandler")," utility acts as a proxy that routes queries to the correct LMS backend API,\nbased on a regex match on the URL of the course. It is configured via the ",(0,r.kt)("inlineCode",{parentName:"p"},"RICHIE_LMS_BACKENDS"),"\nsetting. As an example, here is how it would be configured to connect to an Ironwood OpenEdX\ninstance hosted on ",(0,r.kt)("inlineCode",{parentName:"p"},"https://lms.example.com"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-python"},'RICHIE_LMS_BACKENDS=[\n {\n "BASE_URL": "https://lms.example.com",\n # Django\n "BACKEND": "richie.apps.courses.lms.edx.EdXLMSBackend",\n "COURSE_REGEX": r"^https://lms\\.example\\.com/courses/(?P.*)/course/?$",\n # ReactJS\n "JS_BACKEND": "openedx-hawthorn",\n "JS_COURSE_REGEX": r"^https://lms\\.example\\.com/courses/(.*)/course/?$",\n # Course runs synchronization\n "COURSE_RUN_SYNC_NO_UPDATE_FIELDS": [],\n "DEFAULT_COURSE_RUN_SYNC_MODE": "sync_to_public",\n },\n]\n')),(0,r.kt)("p",null,"The following should help you understand how to configure this setting:"),(0,r.kt)("h3",{id:"base_url"},"BASE_URL"),(0,r.kt)("p",null,"The base url on which the OpenEdX instance is hosted. This is used to construct the complete url\nof the API endpoint on which the enrollment request is made by Richie's frontend application."),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Type: string"),(0,r.kt)("li",{parentName:"ul"},"Required: Yes"),(0,r.kt)("li",{parentName:"ul"},"Value: for example ",(0,r.kt)("a",{parentName:"li",href:"https://lms.example.com"},"https://lms.example.com"))),(0,r.kt)("h3",{id:"backend"},"BACKEND"),(0,r.kt)("p",null,"The path to a Python class serving as LMS backend for the targeted LMS."),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Type: string"),(0,r.kt)("li",{parentName:"ul"},"Required: Yes"),(0,r.kt)("li",{parentName:"ul"},"Value: Richie ships with the following Python backends (custom backends can be written to fit\nanother specific LMS):",(0,r.kt)("ul",{parentName:"li"},(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"richie.apps.courses.lms.edx.EdXLMSBackend"),": backend for OpenEdX"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"richie.apps.courses.lms.base.BaseLMSBackend"),": fake backend for development purposes")))),(0,r.kt)("h3",{id:"course_regex"},"COURSE_REGEX"),(0,r.kt)("p",null,"A Python regex that should match the course syllabus urls of the targeted LMS and return a\n",(0,r.kt)("inlineCode",{parentName:"p"},"course_id")," named group on the id of the course extracted from these urls."),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Type: string"),(0,r.kt)("li",{parentName:"ul"},"Required: Yes"),(0,r.kt)("li",{parentName:"ul"},"Value: for example ",(0,r.kt)("inlineCode",{parentName:"li"},"^.*/courses/(?P.*)/course/?$"))),(0,r.kt)("h3",{id:"js_backend"},"JS_BACKEND"),(0,r.kt)("p",null,"The name of the ReactJS backend to use for the targeted LMS."),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Type: string"),(0,r.kt)("li",{parentName:"ul"},"Required: Yes"),(0,r.kt)("li",{parentName:"ul"},"Value: Richie ships with the following Javascript backends (custom backends can be written to\nfit another specific LMS):",(0,r.kt)("ul",{parentName:"li"},(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"openedx-dogwood"),": backend for OpenEdX versions equal to ",(0,r.kt)("inlineCode",{parentName:"li"},"dogwood")," or ",(0,r.kt)("inlineCode",{parentName:"li"},"eucalyptus")),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"openedx-hawthorn"),": backend for OpenEdX versions equal to ",(0,r.kt)("inlineCode",{parentName:"li"},"hawthorn")," or higher"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"openedx-fonzie"),": backend for OpenEdX via ",(0,r.kt)("a",{parentName:"li",href:"https://github.com/openfun/fonzie"},"Fonzie"),"\n(extra user info and JWT tokens)"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"dummy"),": fake backend for development purposes")))),(0,r.kt)("h3",{id:"js_course_regex"},"JS_COURSE_REGEX"),(0,r.kt)("p",null,"A Javascript regex that should match the course syllabus urls of the targeted LMS and return an\nunnamed group on the id of the course extracted from these urls."),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Type: string"),(0,r.kt)("li",{parentName:"ul"},"Required: Yes"),(0,r.kt)("li",{parentName:"ul"},"Value: for example ",(0,r.kt)("inlineCode",{parentName:"li"},"^.*/courses/(.*)/course/?$"))),(0,r.kt)("h3",{id:"default_course_run_sync_mode"},"DEFAULT_COURSE_RUN_SYNC_MODE"),(0,r.kt)("p",null,"When a course run is created, this setting is used to set the value of the ",(0,r.kt)("inlineCode",{parentName:"p"},"sync_mode")," field.\nThis value defines how the course runs synchronization script will impact this course run after\ncreation."),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Type: enum(string)"),(0,r.kt)("li",{parentName:"ul"},"Required: No"),(0,r.kt)("li",{parentName:"ul"},"Value: possible values are ",(0,r.kt)("inlineCode",{parentName:"li"},"manual"),", ",(0,r.kt)("inlineCode",{parentName:"li"},"sync_to_draft")," and ",(0,r.kt)("inlineCode",{parentName:"li"},"sync_to_public"),(0,r.kt)("ul",{parentName:"li"},(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"manual"),": this course run is ignored by the course runs synchronization script"),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"sync_to_draft"),": only the draft version of this course run is synchronized. A manual\npublication is necessary for the update to be visible on the public site."),(0,r.kt)("li",{parentName:"ul"},(0,r.kt)("inlineCode",{parentName:"li"},"sync_to_public"),": the public version of this course run is updated by the synchronization\nscript. As a results, updates are directly visible on the public site without further\npublication by a staff user in Richie.")))),(0,r.kt)("h3",{id:"course_run_sync_no_update_fields"},"COURSE_RUN_SYNC_NO_UPDATE_FIELDS"),(0,r.kt)("p",null,"A list of fields that must only be set the first time a course run is synchronized. During this\nfirst synchronization, a new course run is created in Richie and all fields sent to the API\nendpoint via the payload are set on the object. For subsequent synchronization calls, the fields\nlisted in this setting are ignored and not synchronized. This can be used to allow modifying some\nfields manually in Richie regardless of what OpenEdX sends after an initial value is set."),(0,r.kt)("p",null,"Note that this setting is only effective for course runs with the ",(0,r.kt)("inlineCode",{parentName:"p"},"sync_mode")," field set to a\nvalue other then ",(0,r.kt)("inlineCode",{parentName:"p"},"manual"),"."),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"Type: enum(string)"),(0,r.kt)("li",{parentName:"ul"},"Required: No"),(0,r.kt)("li",{parentName:"ul"},"Value: for example ",'["languages"]')),(0,r.kt)("h2",{id:"technical-support"},"Technical support"),(0,r.kt)("p",null,"If you encounter an issue with this documentation or the backends included in Richie, please\n",(0,r.kt)("a",{parentName:"p",href:"https://github.com/openfun/richie/issues"},"open an issue on our repository"),"."),(0,r.kt)("p",null,"If you need a custom backend, you can ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/openfun/richie/pulls"},"submit a PR")," or\n",(0,r.kt)("a",{parentName:"p",href:"https://github.com/openfun/richie/issues"},"open an issue")," and we will consider adding it."))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/43dd260b.e5f7cca6.js b/assets/js/43dd260b.e5f7cca6.js new file mode 100644 index 0000000000..87cd06dcbe --- /dev/null +++ b/assets/js/43dd260b.e5f7cca6.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[23627],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>h});var r=n(67294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var c=r.createContext({}),s=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},u=function(e){var t=s(e.components);return r.createElement(c.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},p=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,c=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),p=s(n),h=a,f=p["".concat(c,".").concat(h)]||p[h]||d[h]||o;return n?r.createElement(f,i(i({ref:t},u),{},{components:n})):r.createElement(f,i({ref:t},u))}));function h(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=p;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l.mdxType="string"==typeof e?e:a,i[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>u,contentTitle:()=>c,default:()=>h,frontMatter:()=>l,metadata:()=>s,toc:()=>d});var r=n(83117),a=n(80102),o=(n(67294),n(3905)),i=["components"],l={id:"internationalization",title:"Internationalization",sidebar_label:"I18n"},c=void 0,s={unversionedId:"internationalization",id:"version-2.22.0/internationalization",title:"Internationalization",description:"richie has built-in localization and internationalization:",source:"@site/versioned_docs/version-2.22.0/internationalization.md",sourceDirName:".",slug:"/internationalization",permalink:"/docs/internationalization",draft:!1,tags:[],version:"2.22.0",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1682069684,formattedLastUpdatedAt:"Apr 21, 2023",frontMatter:{id:"internationalization",title:"Internationalization",sidebar_label:"I18n"},sidebar:"docs",previous:{title:"Frontend overrides",permalink:"/docs/frontend-overrides"},next:{title:"LMS connection",permalink:"/docs/lms-connection"}},u={},d=[{value:"Contributing as a translator or proof-reader",id:"contributing-as-a-translator-or-proof-reader",level:2},{value:"Sign-up on Crowdin",id:"sign-up-on-crowdin",level:3},{value:"Join the Richie project",id:"join-the-richie-project",level:3},{value:"Add a new language",id:"add-a-new-language",level:3}],p={toc:d};function h(e){var t=e.components,l=(0,a.Z)(e,i);return(0,o.kt)("wrapper",(0,r.Z)({},p,l,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,(0,o.kt)("inlineCode",{parentName:"p"},"richie")," has built-in localization and internationalization:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,"),(0,o.kt)("li",{parentName:"ul"},"On the frontend, we use React Intl.")),(0,o.kt)("h2",{id:"contributing-as-a-translator-or-proof-reader"},"Contributing as a translator or proof-reader"),(0,o.kt)("p",null,"We use the ",(0,o.kt)("a",{parentName:"p",href:"https://crowdin.com"},"Crowdin")," web platform to translate Richie to different languages.\nAll translations are hosted at ",(0,o.kt)("a",{parentName:"p",href:"https://i18n.richie.education"},"https://i18n.richie.education"),", which allows translators and\nproof-readers to contribute on translations in the languages they master."),(0,o.kt)("h3",{id:"sign-up-on-crowdin"},"Sign-up on Crowdin"),(0,o.kt)("p",null,"If you don't have an account on Crowdin already, go to ",(0,o.kt)("a",{parentName:"p",href:"https://accounts.crowdin.com/register"},"https://accounts.crowdin.com/register")," and\nfill out the form to create a free account."),(0,o.kt)("h3",{id:"join-the-richie-project"},"Join the Richie project"),(0,o.kt)("p",null,"Now that you have an account on Crowdin,\n",(0,o.kt)("a",{parentName:"p",href:"https://crowdin.com/project/richie"},'look for the project called "Richie"'),', select the language\non which you wish to contribute and click the "Join" button as demonstrated below:'),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"How to join Richie on Crowdin",src:n(56374).Z,width:"2462",height:"1506"})),(0,o.kt)("p",null,"We will then review you application and you should soon start translating strings!"),(0,o.kt)("p",null,"For more information on how Crowdin works, you can refer to\n",(0,o.kt)("a",{parentName:"p",href:"https://support.crowdin.com"},"their documentation"),"."),(0,o.kt)("h3",{id:"add-a-new-language"},"Add a new language"),(0,o.kt)("p",null,'If Richie is not yet translated in the language you want, let us know by clicking the "contact"\nlink on ',(0,o.kt)("a",{parentName:"p",href:"https://i18n.richie.education"},"Richie's Crowdin profile page")," and we will consider adding\nit."),(0,o.kt)("p",null,"If you request a new language, the Richie community will expect you to keep this language\nup-to-date each time strings are modified or new strings are added, and this before each\nrelease."),(0,o.kt)("p",null,"Before asking for a new language, make sure it does not already exist. If your language already\nexists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider\ncontributing on the existing language if your resources to contribute are limited."))}h.isMDXComponent=!0},56374:(e,t,n)=>{n.d(t,{Z:()=>r});const r=n.p+"assets/images/crowdin-join-richie-fd505b1a132bafb2bdafc715649a7c0f.gif"}}]); \ No newline at end of file diff --git a/assets/js/4b3d2ce7.4f37e633.js b/assets/js/4b3d2ce7.4f37e633.js deleted file mode 100644 index 6ad79aab04..0000000000 --- a/assets/js/4b3d2ce7.4f37e633.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[34221],{3905:(e,n,t)=>{t.d(n,{Zo:()=>p,kt:()=>m});var o=t(67294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);n&&(o=o.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,o)}return t}function a(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var s=o.createContext({}),c=function(e){var n=o.useContext(s),t=n;return e&&(t="function"==typeof e?e(n):a(a({},n),e)),t},p=function(e){var n=c(e.components);return o.createElement(s.Provider,{value:n},e.children)},u={inlineCode:"code",wrapper:function(e){var n=e.children;return o.createElement(o.Fragment,{},n)}},d=o.forwardRef((function(e,n){var t=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),d=c(t),m=i,h=d["".concat(s,".").concat(m)]||d[m]||u[m]||r;return t?o.createElement(h,a(a({ref:n},p),{},{components:t})):o.createElement(h,a({ref:n},p))}));function m(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var r=t.length,a=new Array(r);a[0]=d;var l={};for(var s in n)hasOwnProperty.call(n,s)&&(l[s]=n[s]);l.originalType=e,l.mdxType="string"==typeof e?e:i,a[1]=l;for(var c=2;c{t.r(n),t.d(n,{assets:()=>p,contentTitle:()=>s,default:()=>m,frontMatter:()=>l,metadata:()=>c,toc:()=>u});var o=t(83117),i=t(80102),r=(t(67294),t(3905)),a=["components"],l={id:"lms-connection",title:"Connecting Richie with one or more LMS",sidebar_label:"LMS connection"},s=void 0,c={unversionedId:"lms-connection",id:"version-2.21.1/lms-connection",title:"Connecting Richie with one or more LMS",description:"Connecting Richie to an LMS",source:"@site/versioned_docs/version-2.21.1/lms-connection.md",sourceDirName:".",slug:"/lms-connection",permalink:"/docs/lms-connection",draft:!1,tags:[],version:"2.21.1",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1680624606,formattedLastUpdatedAt:"Apr 4, 2023",frontMatter:{id:"lms-connection",title:"Connecting Richie with one or more LMS",sidebar_label:"LMS connection"},sidebar:"docs",previous:{title:"I18n",permalink:"/docs/internationalization"},next:{title:"Web Analytics",permalink:"/docs/web-analytics"}},p={},u=[{value:"Connecting Richie to an LMS",id:"connecting-richie-to-an-lms",level:2},{value:"1. Displaying connection status",id:"1-displaying-connection-status",level:3},{value:"2. Seamless enrollment",id:"2-seamless-enrollment",level:3},{value:"3. Synchronizing course runs details",id:"3-synchronizing-course-runs-details",level:3},{value:"4. Joanie, the enrollment manager",id:"4-joanie-the-enrollment-manager",level:3},{value:"Development",id:"development",level:2}],d={toc:u};function m(e){var n=e.components,t=(0,i.Z)(e,a);return(0,r.kt)("wrapper",(0,o.Z)({},d,t,{components:n,mdxType:"MDXLayout"}),(0,r.kt)("h2",{id:"connecting-richie-to-an-lms"},"Connecting Richie to an LMS"),(0,r.kt)("p",null,"Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated\nseamless experience."),(0,r.kt)("p",null,"As of today, each approach has been implemented for OpenEdX but the same could be done for\nother LMSes like Moodle, at the cost of minor adaptations."),(0,r.kt)("h3",{id:"1-displaying-connection-status"},"1. Displaying connection status"),(0,r.kt)("p",null,"OpenEdX can be configured to allow CORS requests. Doing so allows Richie to retrieve a user's\nconnection status from OpenEdx and display the user's profile information directly on the Richie\nsite: username, dashboard url, etc."),(0,r.kt)("p",null,"In this approach, a user visiting your Richie site and trying to signup or login, is sent to the\nOpenEdX site for authentication and is redirected back to the Richie site upon successful login."),(0,r.kt)("p",null,"You can see this in action on ",(0,r.kt)("a",{parentName:"p",href:"https://www.fun-mooc.fr"},"https://www.fun-mooc.fr"),"."),(0,r.kt)("p",null,"We provide detailed instructions on\n",(0,r.kt)("a",{parentName:"p",href:"/docs/displaying-connection-status"},"how to configure displaying OpenEdX connection status in Richie"),"."),(0,r.kt)("h3",{id:"2-seamless-enrollment"},"2. Seamless enrollment"),(0,r.kt)("p",null,"Thanks to OpenEdX's enrollment API, it is possible to let users enroll on course runs without\nleaving Richie."),(0,r.kt)("p",null,"You can see this in action on ",(0,r.kt)("a",{parentName:"p",href:"https://www.fun-mooc.fr"},"https://www.fun-mooc.fr"),"."),(0,r.kt)("blockquote",null,(0,r.kt)("p",{parentName:"blockquote"},"This feature requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that\nare both subdomains of the same root domain, e.g. ",(0,r.kt)("inlineCode",{parentName:"p"},"richie.example.com")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"lms.example.com"),".")),(0,r.kt)("p",null,"You should read our guide on ",(0,r.kt)("a",{parentName:"p",href:"lms-backends"},"how to use OpenEdX as LMS backend for Richie"),"."),(0,r.kt)("h3",{id:"3-synchronizing-course-runs-details"},"3. Synchronizing course runs details"),(0,r.kt)("p",null,"Course runs in Richie can be handled manually, filling all fields via the DjangoCMS front-end\nediting interface. But a better way to handle course runs is to synchronize them automatically\nfrom your LMS using the course run synchronization API."),(0,r.kt)("p",null,"Please refer to our guide on ",(0,r.kt)("a",{parentName:"p",href:"synchronizing-course-runs"},"how to synchronize course runs between Richie and OpenEdx")),(0,r.kt)("h3",{id:"4-joanie-the-enrollment-manager"},"4. Joanie, the enrollment manager"),(0,r.kt)("p",null,"For more advanced use cases, we have started a new project called ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/openfun/joanie"},"Joanie")," which acts as an\nenrollment manager for Richie."),(0,r.kt)("p",null,"Authentication in Joanie is done via JWT Tokens for maximum flexibility and decoupling in\nidentity management."),(0,r.kt)("p",null,"The project started early 2021, but over time, Joanie will handle:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"paid enrollments / certification"),(0,r.kt)("li",{parentName:"ul"},"micro-credentials"),(0,r.kt)("li",{parentName:"ul"},"user dashboard"),(0,r.kt)("li",{parentName:"ul"},"cohorts management (academic or B2B)"),(0,r.kt)("li",{parentName:"ul"},"multi-LMS catalogs"),(0,r.kt)("li",{parentName:"ul"},"time based enrollment")),(0,r.kt)("h2",{id:"development"},"Development"),(0,r.kt)("p",null,"For development purposes, the docker-compose project provided on\n",(0,r.kt)("a",{parentName:"p",href:"https://github.com/openfun/richie"},"Richie's code repository")," is pre-configured to connect\nwith an OpenEdx instance started with\n",(0,r.kt)("a",{parentName:"p",href:"https://github.com/openfun/openedx-docker"},"OpenEdx Docker"),", which provides a ready-to-use\ndocker-compose stack of OpenEdx in several flavors. Head over to\n",(0,r.kt)("a",{parentName:"p",href:"https://github.com/openfun/openedx-docker#readme"},"OpenEdx Docker README")," for instructions on how to bootstrap an OpenEdX instance."),(0,r.kt)("p",null,"Now, start both the OpenEdX and Richie projects separately with ",(0,r.kt)("inlineCode",{parentName:"p"},"make run"),"."),(0,r.kt)("p",null,"Richie should respond on ",(0,r.kt)("inlineCode",{parentName:"p"},"http://localhost:8070"),", OpenEdx on ",(0,r.kt)("inlineCode",{parentName:"p"},"http://localhost:8073")," and both\napps should be able to communicate with each other via the network bridge defined in\ndocker-compose."),(0,r.kt)("p",null,"If you want to activate ",(0,r.kt)("a",{parentName:"p",href:"#2-seamless-enrollment"},"seamless enrollment")," locally for development,\nyou will need to set up TLS domains for both Richie and OpenEdX. To do this, head over to our\nguide on ",(0,r.kt)("a",{parentName:"p",href:"tls-connection"},"setting-up TLS connections for Richie and OpenEdX"),"."))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/4b3d2ce7.d67d80b5.js b/assets/js/4b3d2ce7.d67d80b5.js new file mode 100644 index 0000000000..75e7b80598 --- /dev/null +++ b/assets/js/4b3d2ce7.d67d80b5.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[34221],{3905:(e,n,t)=>{t.d(n,{Zo:()=>p,kt:()=>m});var o=t(67294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);n&&(o=o.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,o)}return t}function a(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var s=o.createContext({}),c=function(e){var n=o.useContext(s),t=n;return e&&(t="function"==typeof e?e(n):a(a({},n),e)),t},p=function(e){var n=c(e.components);return o.createElement(s.Provider,{value:n},e.children)},u={inlineCode:"code",wrapper:function(e){var n=e.children;return o.createElement(o.Fragment,{},n)}},d=o.forwardRef((function(e,n){var t=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),d=c(t),m=i,h=d["".concat(s,".").concat(m)]||d[m]||u[m]||r;return t?o.createElement(h,a(a({ref:n},p),{},{components:t})):o.createElement(h,a({ref:n},p))}));function m(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var r=t.length,a=new Array(r);a[0]=d;var l={};for(var s in n)hasOwnProperty.call(n,s)&&(l[s]=n[s]);l.originalType=e,l.mdxType="string"==typeof e?e:i,a[1]=l;for(var c=2;c{t.r(n),t.d(n,{assets:()=>p,contentTitle:()=>s,default:()=>m,frontMatter:()=>l,metadata:()=>c,toc:()=>u});var o=t(83117),i=t(80102),r=(t(67294),t(3905)),a=["components"],l={id:"lms-connection",title:"Connecting Richie with one or more LMS",sidebar_label:"LMS connection"},s=void 0,c={unversionedId:"lms-connection",id:"version-2.21.1/lms-connection",title:"Connecting Richie with one or more LMS",description:"Connecting Richie to an LMS",source:"@site/versioned_docs/version-2.21.1/lms-connection.md",sourceDirName:".",slug:"/lms-connection",permalink:"/docs/2.21.1/lms-connection",draft:!1,tags:[],version:"2.21.1",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1680624606,formattedLastUpdatedAt:"Apr 4, 2023",frontMatter:{id:"lms-connection",title:"Connecting Richie with one or more LMS",sidebar_label:"LMS connection"},sidebar:"docs",previous:{title:"I18n",permalink:"/docs/2.21.1/internationalization"},next:{title:"Web Analytics",permalink:"/docs/2.21.1/web-analytics"}},p={},u=[{value:"Connecting Richie to an LMS",id:"connecting-richie-to-an-lms",level:2},{value:"1. Displaying connection status",id:"1-displaying-connection-status",level:3},{value:"2. Seamless enrollment",id:"2-seamless-enrollment",level:3},{value:"3. Synchronizing course runs details",id:"3-synchronizing-course-runs-details",level:3},{value:"4. Joanie, the enrollment manager",id:"4-joanie-the-enrollment-manager",level:3},{value:"Development",id:"development",level:2}],d={toc:u};function m(e){var n=e.components,t=(0,i.Z)(e,a);return(0,r.kt)("wrapper",(0,o.Z)({},d,t,{components:n,mdxType:"MDXLayout"}),(0,r.kt)("h2",{id:"connecting-richie-to-an-lms"},"Connecting Richie to an LMS"),(0,r.kt)("p",null,"Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated\nseamless experience."),(0,r.kt)("p",null,"As of today, each approach has been implemented for OpenEdX but the same could be done for\nother LMSes like Moodle, at the cost of minor adaptations."),(0,r.kt)("h3",{id:"1-displaying-connection-status"},"1. Displaying connection status"),(0,r.kt)("p",null,"OpenEdX can be configured to allow CORS requests. Doing so allows Richie to retrieve a user's\nconnection status from OpenEdx and display the user's profile information directly on the Richie\nsite: username, dashboard url, etc."),(0,r.kt)("p",null,"In this approach, a user visiting your Richie site and trying to signup or login, is sent to the\nOpenEdX site for authentication and is redirected back to the Richie site upon successful login."),(0,r.kt)("p",null,"You can see this in action on ",(0,r.kt)("a",{parentName:"p",href:"https://www.fun-mooc.fr"},"https://www.fun-mooc.fr"),"."),(0,r.kt)("p",null,"We provide detailed instructions on\n",(0,r.kt)("a",{parentName:"p",href:"/docs/2.21.1/displaying-connection-status"},"how to configure displaying OpenEdX connection status in Richie"),"."),(0,r.kt)("h3",{id:"2-seamless-enrollment"},"2. Seamless enrollment"),(0,r.kt)("p",null,"Thanks to OpenEdX's enrollment API, it is possible to let users enroll on course runs without\nleaving Richie."),(0,r.kt)("p",null,"You can see this in action on ",(0,r.kt)("a",{parentName:"p",href:"https://www.fun-mooc.fr"},"https://www.fun-mooc.fr"),"."),(0,r.kt)("blockquote",null,(0,r.kt)("p",{parentName:"blockquote"},"This feature requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that\nare both subdomains of the same root domain, e.g. ",(0,r.kt)("inlineCode",{parentName:"p"},"richie.example.com")," and ",(0,r.kt)("inlineCode",{parentName:"p"},"lms.example.com"),".")),(0,r.kt)("p",null,"You should read our guide on ",(0,r.kt)("a",{parentName:"p",href:"lms-backends"},"how to use OpenEdX as LMS backend for Richie"),"."),(0,r.kt)("h3",{id:"3-synchronizing-course-runs-details"},"3. Synchronizing course runs details"),(0,r.kt)("p",null,"Course runs in Richie can be handled manually, filling all fields via the DjangoCMS front-end\nediting interface. But a better way to handle course runs is to synchronize them automatically\nfrom your LMS using the course run synchronization API."),(0,r.kt)("p",null,"Please refer to our guide on ",(0,r.kt)("a",{parentName:"p",href:"synchronizing-course-runs"},"how to synchronize course runs between Richie and OpenEdx")),(0,r.kt)("h3",{id:"4-joanie-the-enrollment-manager"},"4. Joanie, the enrollment manager"),(0,r.kt)("p",null,"For more advanced use cases, we have started a new project called ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/openfun/joanie"},"Joanie")," which acts as an\nenrollment manager for Richie."),(0,r.kt)("p",null,"Authentication in Joanie is done via JWT Tokens for maximum flexibility and decoupling in\nidentity management."),(0,r.kt)("p",null,"The project started early 2021, but over time, Joanie will handle:"),(0,r.kt)("ul",null,(0,r.kt)("li",{parentName:"ul"},"paid enrollments / certification"),(0,r.kt)("li",{parentName:"ul"},"micro-credentials"),(0,r.kt)("li",{parentName:"ul"},"user dashboard"),(0,r.kt)("li",{parentName:"ul"},"cohorts management (academic or B2B)"),(0,r.kt)("li",{parentName:"ul"},"multi-LMS catalogs"),(0,r.kt)("li",{parentName:"ul"},"time based enrollment")),(0,r.kt)("h2",{id:"development"},"Development"),(0,r.kt)("p",null,"For development purposes, the docker-compose project provided on\n",(0,r.kt)("a",{parentName:"p",href:"https://github.com/openfun/richie"},"Richie's code repository")," is pre-configured to connect\nwith an OpenEdx instance started with\n",(0,r.kt)("a",{parentName:"p",href:"https://github.com/openfun/openedx-docker"},"OpenEdx Docker"),", which provides a ready-to-use\ndocker-compose stack of OpenEdx in several flavors. Head over to\n",(0,r.kt)("a",{parentName:"p",href:"https://github.com/openfun/openedx-docker#readme"},"OpenEdx Docker README")," for instructions on how to bootstrap an OpenEdX instance."),(0,r.kt)("p",null,"Now, start both the OpenEdX and Richie projects separately with ",(0,r.kt)("inlineCode",{parentName:"p"},"make run"),"."),(0,r.kt)("p",null,"Richie should respond on ",(0,r.kt)("inlineCode",{parentName:"p"},"http://localhost:8070"),", OpenEdx on ",(0,r.kt)("inlineCode",{parentName:"p"},"http://localhost:8073")," and both\napps should be able to communicate with each other via the network bridge defined in\ndocker-compose."),(0,r.kt)("p",null,"If you want to activate ",(0,r.kt)("a",{parentName:"p",href:"#2-seamless-enrollment"},"seamless enrollment")," locally for development,\nyou will need to set up TLS domains for both Richie and OpenEdX. To do this, head over to our\nguide on ",(0,r.kt)("a",{parentName:"p",href:"tls-connection"},"setting-up TLS connections for Richie and OpenEdX"),"."))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/4ee76bca.8c6cf3ab.js b/assets/js/4ee76bca.8c6cf3ab.js deleted file mode 100644 index 77fe82d025..0000000000 --- a/assets/js/4ee76bca.8c6cf3ab.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkrichie_education_docs=self.webpackChunkrichie_education_docs||[]).push([[21479],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>m});var a=n(67294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function l(e){for(var t=1;t=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=a.createContext({}),c=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},p=function(e){var t=c(e.components);return a.createElement(s.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},u=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,o=e.originalType,s=e.parentName,p=r(e,["components","mdxType","originalType","parentName"]),u=c(n),m=i,g=u["".concat(s,".").concat(m)]||u[m]||d[m]||o;return n?a.createElement(g,l(l({ref:t},p),{},{components:n})):a.createElement(g,l({ref:t},p))}));function m(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=n.length,l=new Array(o);l[0]=u;var r={};for(var s in t)hasOwnProperty.call(t,s)&&(r[s]=t[s]);r.originalType=e,r.mdxType="string"==typeof e?e:i,l[1]=r;for(var c=2;c{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>s,default:()=>m,frontMatter:()=>r,metadata:()=>c,toc:()=>d});var a=n(83117),i=n(80102),o=(n(67294),n(3905)),l=["components"],r={id:"web-analytics",title:"Add web analytics to your site",sidebar_label:"Web Analytics"},s=void 0,c={unversionedId:"web-analytics",id:"version-2.21.1/web-analytics",title:"Add web analytics to your site",description:"Richie has native support to Google Universal Analytics and Google Tag Manager Web Analytics solutions.",source:"@site/versioned_docs/version-2.21.1/web-analytics.md",sourceDirName:".",slug:"/web-analytics",permalink:"/docs/web-analytics",draft:!1,tags:[],version:"2.21.1",lastUpdatedBy:"jbpenrath",lastUpdatedAt:1680624606,formattedLastUpdatedAt:"Apr 4, 2023",frontMatter:{id:"web-analytics",title:"Add web analytics to your site",sidebar_label:"Web Analytics"},sidebar:"docs",previous:{title:"LMS connection",permalink:"/docs/lms-connection"},next:{title:"Installation",permalink:"/docs/installation"}},p={},d=[{value:"Google Universal Analytics",id:"google-universal-analytics",level:2},{value:"Google Tag",id:"google-tag",level:2},{value:"Google Tag Manager",id:"google-tag-manager",level:2},{value:"Multiple Web Analytics at the same time",id:"multiple-web-analytics-at-the-same-time",level:2},{value:"Location of the web analytics javascript",id:"location-of-the-web-analytics-javascript",level:2},{value:"Add a new Web Analytics solution",id:"add-a-new-web-analytics-solution",level:2}],u={toc:d};function m(e){var t=e.components,n=(0,i.Z)(e,l);return(0,o.kt)("wrapper",(0,a.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,"Richie has native support to ",(0,o.kt)("a",{parentName:"p",href:"#google-analytics"},"Google Universal Analytics")," and ",(0,o.kt)("a",{parentName:"p",href:"#google-tag-manager"},"Google Tag Manager")," Web Analytics solutions.\nThe purpose of this file is to explain how you can enable one of the supported Web Analytics providers\nand how you can extend Richie with an alternative solution."),(0,o.kt)("h2",{id:"google-universal-analytics"},"Google Universal Analytics"),(0,o.kt)("p",null,"Next, it is described how you can configure the ",(0,o.kt)("strong",{parentName:"p"},"Google Universal Analytics")," on your Richie site."),(0,o.kt)("p",null,"Add the ",(0,o.kt)("inlineCode",{parentName:"p"},"WEB_ANALYTICS")," setting, with the Google Universal Analytics configuration. From the next example replace ",(0,o.kt)("inlineCode",{parentName:"p"},"TRACKING_ID")," with your tracking id code."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-python"},"{\n 'google_universal_analytics': {\n 'tracking_id': 'TRACKING_ID',\n }\n}\n")),(0,o.kt)("p",null,"The current Google Universal Analytics implementation also includes custom dimensions. Those dimensions permit you to create further analyses on Google Universal Analytics or even use them to create custom reports.\nCustom dimensions with a value as example:"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Organizations codes - ",(0,o.kt)("inlineCode",{parentName:"li"},"UNIV_LISBON | UNIV_PORTO")),(0,o.kt)("li",{parentName:"ul"},"Course code - ",(0,o.kt)("inlineCode",{parentName:"li"},"COURSE_XPTO")),(0,o.kt)("li",{parentName:"ul"},"Course runs titles - ",(0,o.kt)("inlineCode",{parentName:"li"},"Summer edition | Winter edition")),(0,o.kt)("li",{parentName:"ul"},"Course runs resource links - ",(0,o.kt)("inlineCode",{parentName:"li"},"http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/info")),(0,o.kt)("li",{parentName:"ul"},"Page title - ",(0,o.kt)("inlineCode",{parentName:"li"},"Introduction to Programming"))),(0,o.kt)("h2",{id:"google-tag"},"Google Tag"),(0,o.kt)("p",null,"It is possible to configure the ",(0,o.kt)("strong",{parentName:"p"},"Google Tag"),", ",(0,o.kt)("inlineCode",{parentName:"p"},"gtag.js"),", on your Richie site."),(0,o.kt)("p",null,"Add the ",(0,o.kt)("inlineCode",{parentName:"p"},"WEB_ANALYTICS")," setting, with the Google Tag configuration like for example:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-python"},"{\n 'google_tag': {\n 'tracking_id': 'TRACKING_ID',\n }\n}\n")),(0,o.kt)("p",null,"And don't forget to replace the ",(0,o.kt)("inlineCode",{parentName:"p"},"TRACKING_ID")," with your tracking id/code from Google Ads, Google Analytics, or other Google product compatible with the ",(0,o.kt)("inlineCode",{parentName:"p"},"gtag.js"),"."),(0,o.kt)("p",null,"The Google Tag is initialized with custom dimensions like the ",(0,o.kt)("a",{parentName:"p",href:"#google-analytics"},"Google Universal Analytics"),"."),(0,o.kt)("h2",{id:"google-tag-manager"},"Google Tag Manager"),(0,o.kt)("p",null,"Next, it is described how you can configure the ",(0,o.kt)("strong",{parentName:"p"},"Google Tag Manager"),", ",(0,o.kt)("inlineCode",{parentName:"p"},"gtm.js"),", on your Richie site."),(0,o.kt)("p",null,"Add the ",(0,o.kt)("inlineCode",{parentName:"p"},"WEB_ANALYTICS")," setting, with the Google Tag Manager configuration, for example:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-python"},"{\n 'google_tag_manager': {\n 'tracking_id': 'TRACKING_ID',\n }\n}\n")),(0,o.kt)("p",null,"And don't forget to replace the ",(0,o.kt)("inlineCode",{parentName:"p"},"TRACKING_ID")," with your ",(0,o.kt)("inlineCode",{parentName:"p"},"GTM")," tracking id/code."),(0,o.kt)("p",null,"The current Google Tag Manager implementation also defines a custom dimensions like the ",(0,o.kt)("a",{parentName:"p",href:"#google-analytics"},"Google Universal Analytics"),"."),(0,o.kt)("p",null,"If you want to use the Environments feature of the Google Tag Manager, you need to include the ",(0,o.kt)("inlineCode",{parentName:"p"},"environment")," key with its value on ",(0,o.kt)("inlineCode",{parentName:"p"},"google_tag_manager")," dict inside the ",(0,o.kt)("inlineCode",{parentName:"p"},"WEB_ANALYTICS")," setting. "),(0,o.kt)("p",null,(0,o.kt)("em",{parentName:"p"},"The environments feature in Google Tag Manager is ideal for organizations that want to preview their container changes in a test environment before those changes are published"),"."),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-python"},"{\n 'google_tag_manager': {\n 'tracking_id': 'TRACKING_ID',\n 'environment': '>m_auth=aaaaaaaaaaaaaaaa>m_preview=env-99>m_cookies_win=x';\n }\n}\n")),(0,o.kt)("h2",{id:"multiple-web-analytics-at-the-same-time"},"Multiple Web Analytics at the same time"),(0,o.kt)("p",null,"It is possible to configure several web analytics solutions at the same time or the same solution with different tracking identifications."),(0,o.kt)("p",null,(0,o.kt)("inlineCode",{parentName:"p"},"WEB_ANALYTICS")," setting example to have both Google Universal Analytics and Google Tag Manager:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-python"},"{\n 'google_universal_analytics': {\n 'tracking_id': 'UA-TRACKING_ID',\n },\n 'google_tag_manager': {\n 'tracking_id': 'GTM-TRACKING_ID',\n }\n}\n")),(0,o.kt)("h2",{id:"location-of-the-web-analytics-javascript"},"Location of the web analytics javascript"),(0,o.kt)("p",null,"Each web analytics js code can be put on the ",(0,o.kt)("inlineCode",{parentName:"p"},"footer")," (",(0,o.kt)("strong",{parentName:"p"},"default")," value), to put the Javascript on HTML body footer, or ",(0,o.kt)("inlineCode",{parentName:"p"},"header"),", to put the Javascript code at the end of the HTML ",(0,o.kt)("inlineCode",{parentName:"p"},"head"),"."),(0,o.kt)("p",null,"Update the ",(0,o.kt)("inlineCode",{parentName:"p"},"WEB_ANALYTICS")," setting, like:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-python"},"{\n 'google_universal_analytics': {\n 'tracking_id': 'UA-TRACKING_ID',\n 'location': 'footer,\n },\n}\n")),(0,o.kt)("h2",{id:"add-a-new-web-analytics-solution"},"Add a new Web Analytics solution"),(0,o.kt)("p",null,"In this section it's described how you can add support to a different Web Analytics solution."),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"override the ",(0,o.kt)("inlineCode",{parentName:"li"},"richie/web_analytics.html")," template"),(0,o.kt)("li",{parentName:"ul"},"define the ",(0,o.kt)("inlineCode",{parentName:"li"},"WEB_ANALYTICS")," setting with a value that represents your solution, eg. ",(0,o.kt)("inlineCode",{parentName:"li"},"my-custom-web-analytics-software")),(0,o.kt)("li",{parentName:"ul"},"define the ",(0,o.kt)("inlineCode",{parentName:"li"},"WEB_ANALYTICS")," setting with your tracking identification"),(0,o.kt)("li",{parentName:"ul"},"optionally change ",(0,o.kt)("inlineCode",{parentName:"li"},"location")," with ",(0,o.kt)("inlineCode",{parentName:"li"},"footer")," (default) or ",(0,o.kt)("inlineCode",{parentName:"li"},"head")," value")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-python"},"{\n 'my-custom-web-analytics-software': {\n 'tracking_id': 'MY_CUSTOM_TRACKING_ID',\n 'location': 'footer,\n },\n}\n")),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"Example of a ",(0,o.kt)("inlineCode",{parentName:"li"},"richie/web_analytics.html")," file customization that prints to the browser console log the dimension keys and values:")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-javascript"}," - - + +
-
Version: 1.12

Contributing guide

This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

We try to raise our code quality standards and expect contributors to follow the recommandations +

Version: 1.12

Contributing guide

This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

Checking your code

We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

$ make lint-back

We use strict eslint and prettier to check the validity of our frontend code:

$ make lint-front

Running tests

On the backend, we use pytest to run our test suite:

$ make test-back

On the frontend, we use karma to run our test suite:

$ make test-front

Running migrations

The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

$ make migrate

Handling new dependencies

Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

$ make bootstrap

Going further

To see all available commands, run:

$ make

We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

bin
├── exec
├── pylint
├── pytest
└── run

More details and tips & tricks can be found in our development with Docker documentation

- - + + \ No newline at end of file diff --git a/docs/1.12/css-guidelines/index.html b/docs/1.12/css-guidelines/index.html index 71421d5f51..70e967950b 100644 --- a/docs/1.12/css-guidelines/index.html +++ b/docs/1.12/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
-
Version: 1.12

CSS Guidelines

The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

File structuration

Rules should be placed where their purpose is most apparent, and where they are easiest to find.

Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

Code structuration

In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

Following the BEM naming convention, we will write our classes as follows :

.block {}
.block__element {}
.block--modifier {}
  • .block represents the higher level of an abstraction or component.
  • .block__element represents a descendent of .block that helps form .block as a whole.
  • .block--modifier represents a different state or version of .block.

We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

Quick pointers

  • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
  • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
  • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
  • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
  • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

(Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

- - +
Version: 1.12

CSS Guidelines

The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

File structuration

Rules should be placed where their purpose is most apparent, and where they are easiest to find.

Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

Code structuration

In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

Following the BEM naming convention, we will write our classes as follows :

.block {}
.block__element {}
.block--modifier {}
  • .block represents the higher level of an abstraction or component.
  • .block__element represents a descendent of .block that helps form .block as a whole.
  • .block--modifier represents a different state or version of .block.

We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

Quick pointers

  • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
  • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
  • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
  • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
  • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

(Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

+ + \ No newline at end of file diff --git a/docs/1.12/discover/index.html b/docs/1.12/discover/index.html index e880167a0f..f896944be6 100644 --- a/docs/1.12/discover/index.html +++ b/docs/1.12/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
-
Version: 1.12

Getting started with Richie

If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

Login/password are admin/admin. The database is regularly flushed.

Architecture

Richie is a container-native application but can also be installed +

Version: 1.12

Getting started with Richie

If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

Login/password are admin/admin. The database is regularly flushed.

Architecture

Richie is a container-native application but can also be installed on your machine.

For development, the project is defined using a docker-compose file and consists of 4 services:

  • db: the Postgresql database,
  • elasticsearch: the search engine,
  • app: the actual DjangoCMS project with all our application code,
  • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -21,7 +21,7 @@ migration-related issues.

Now that your Docker services are ready to be used, start the full CMS by running:

$ make run

Adding content

Once the CMS is up and running, you can create a superuser account:

$ make superuser

You can create a basic demo site by running:

$ make demo-site

Note that if you don't create the demo site and start from a blank CMS, you will get some errors requesting you to create some required root pages. So it is easier as a first approach to test the CMS with the demo site.

You should be able to view the site at localhost:8070

- - + + \ No newline at end of file diff --git a/docs/1.12/django-react-interop/index.html b/docs/1.12/django-react-interop/index.html index 0114d0b5a2..5be393be2a 100644 --- a/docs/1.12/django-react-interop/index.html +++ b/docs/1.12/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
-
Version: 1.12

Connecting React components with Django

richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

Convention

Therefore, we need a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

We decided to use a specific CSS class name along with its modifiers. We reserve the fun-react class and its modified children for this purpose.

Example

Here is how we would call a "FeaturedCourses" component from a template, a plugin or a snippet:

<div class="fun-react fun-react--featured-courses"></div>

When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

- - +
Version: 1.12

Connecting React components with Django

richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

Convention

Therefore, we need a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

We decided to use a specific CSS class name along with its modifiers. We reserve the fun-react class and its modified children for this purpose.

Example

Here is how we would call a "FeaturedCourses" component from a template, a plugin or a snippet:

<div class="fun-react fun-react--featured-courses"></div>

When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

+ + \ No newline at end of file diff --git a/docs/1.12/docker-development/index.html b/docs/1.12/docker-development/index.html index 39333e7bd1..05fb9aca4c 100644 --- a/docs/1.12/docker-development/index.html +++ b/docs/1.12/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
-
Version: 1.12

Developing Richie with Docker

Now that you have Richie up and running, you can start working with it.

Settings

Settings are defined using Django +

Version: 1.12

Developing Richie with Docker

Now that you have Richie up and running, you can start working with it.

Settings

Settings are defined using Django Configurations for different environments:

  • Development: settings for development on developers' local environment,
  • Test: settings used to run our test suite,
  • ContinousIntegration: settings used on the continuous integration platform,
  • Feature: settings for deployment of each developers' feature branches,
  • Staging: settings for deployment to the staging environment,
  • PreProduction: settings for deployment to the pre-production environment,
  • Production: settings for deployment to the production environment.

The Development environment is defined as the default environment.

Front-end tools

If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

# Start the Sass watcher
$ make watch-sass

# In a new terminal or session, start the TypeScript watcher
$ make watch-ts

Container control

You can stop/start/restart a container:

$ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

or stop/start/restart all containers in one command:

$ docker-compose [stop|start|restart]

Debugging

You can easily see the latest logs for a container:

$ docker-compose logs [app|postgresql|mysql|elasticsearch]

Or follow the stream of logs:

$ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

If you need to debug a running container, you can open a Linux shell with the @@ -28,7 +28,7 @@ file

Cleanup

If you work on the Docker configuration and make repeated modifications, remember to periodically clean the unused docker images and containers by running:

$ docker image prune
$ docker container prune

Troubleshooting

ElasticSearch service is always down

If your elasticsearch container fails at booting, checkout the logs via:

$ docker-compose logs elasticsearch

You may see entries similar to:

[1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

In this case, increase virtual memory as follows (UNIX systems):

$ sudo sysctl -w vm/max_map_count=262144
- - + + \ No newline at end of file diff --git a/docs/1.12/native-installation/index.html b/docs/1.12/native-installation/index.html index d647391aeb..a29d32d0e0 100644 --- a/docs/1.12/native-installation/index.html +++ b/docs/1.12/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
-
Version: 1.12

Installing Richie on your machine

This document aims to list all needed steps to have a working Richie +

Version: 1.12

Installing Richie on your machine

This document aims to list all needed steps to have a working Richie installation on your laptop.

A better approach is to use Docker as explained in our guide for container-native instructions.

Installing a fresh server

Version

You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

npm run sass

Run server

Make sure your database is up-to-date before running the application the first time and after each modification to your models:

python sandbox/manage.py migrate

You can create a superuser account:

python sandbox/manage.py createsuperuser

Run the tests

python sandbox/manage.py test

You should now be able to start Django and view the site at localhost:8000

python sandbox/manage.py runserver
- - + + \ No newline at end of file diff --git a/docs/1.13/building-the-frontend/index.html b/docs/1.13/building-the-frontend/index.html index 2b08e2aa15..6aad353de7 100644 --- a/docs/1.13/building-the-frontend/index.html +++ b/docs/1.13/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
-
Version: 1.13

Building Richie's frontend in your own project

Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

Installing richie-education

If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

mkdir -p src/frontend

Then, you need to bootstrap your own frontend project in this new directory.

cd src/frontend
yarn init

With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

yarn add richie-education

In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

"dependencies": {
"richie-education": "1.12.0"
},

Building the Javascript bundle

You are now ready to run your own frontend build. We'll just be using webpack directly.

yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

Here is everything that is happening:

  • yarn webpack — run the webpack CLI;
  • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
  • --output-path ./build — make sure we get our output where we need it to be;
  • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

You can now run your build to change frontend settings or override frontend components with your own.

Building the CSS

If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

mkdir -p src/frontend/scss
touch src/frontend/scss/_mains.scss

Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

@import "richie-education/scss/main";

You are now ready to run the CSS build:

cd src/frontend
yarn build-sass

This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

- - +
Version: 1.13

Building Richie's frontend in your own project

Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

Installing richie-education

If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

mkdir -p src/frontend

Then, you need to bootstrap your own frontend project in this new directory.

cd src/frontend
yarn init

With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

yarn add richie-education

In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

"dependencies": {
"richie-education": "1.12.0"
},

Building the Javascript bundle

You are now ready to run your own frontend build. We'll just be using webpack directly.

yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

Here is everything that is happening:

  • yarn webpack — run the webpack CLI;
  • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
  • --output-path ./build — make sure we get our output where we need it to be;
  • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

You can now run your build to change frontend settings or override frontend components with your own.

Building the CSS

If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

mkdir -p src/frontend/scss
touch src/frontend/scss/_mains.scss

Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

@import "richie-education/scss/main";

You are now ready to run the CSS build:

cd src/frontend
yarn build-sass

This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

+ + \ No newline at end of file diff --git a/docs/1.13/contributing-guide/index.html b/docs/1.13/contributing-guide/index.html index 66c31e36a2..233a91c3a7 100644 --- a/docs/1.13/contributing-guide/index.html +++ b/docs/1.13/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
-
Version: 1.13

Contributing guide

This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

We try to raise our code quality standards and expect contributors to follow the recommandations +

Version: 1.13

Contributing guide

This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

Checking your code

We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

$ make lint-back

We use strict eslint and prettier to check the validity of our frontend code:

$ make lint-front

Running tests

On the backend, we use pytest to run our test suite:

$ make test-back

On the frontend, we use karma to run our test suite:

$ make test-front

Running migrations

The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

$ make migrate

Handling new dependencies

Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

$ make bootstrap

Going further

To see all available commands, run:

$ make

We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

bin
├── exec
├── pylint
├── pytest
└── run

More details and tips & tricks can be found in our development with Docker documentation

- - + + \ No newline at end of file diff --git a/docs/1.13/css-guidelines/index.html b/docs/1.13/css-guidelines/index.html index dceb64da59..b824643789 100644 --- a/docs/1.13/css-guidelines/index.html +++ b/docs/1.13/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
-
Version: 1.13

CSS Guidelines

The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

File structuration

Rules should be placed where their purpose is most apparent, and where they are easiest to find.

Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

Code structuration

In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

Following the BEM naming convention, we will write our classes as follows :

.block {}
.block__element {}
.block--modifier {}
  • .block represents the higher level of an abstraction or component.
  • .block__element represents a descendent of .block that helps form .block as a whole.
  • .block--modifier represents a different state or version of .block.

We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

Quick pointers

  • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
  • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
  • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
  • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
  • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

(Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

- - +
Version: 1.13

CSS Guidelines

The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

File structuration

Rules should be placed where their purpose is most apparent, and where they are easiest to find.

Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

Code structuration

In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

Following the BEM naming convention, we will write our classes as follows :

.block {}
.block__element {}
.block--modifier {}
  • .block represents the higher level of an abstraction or component.
  • .block__element represents a descendent of .block that helps form .block as a whole.
  • .block--modifier represents a different state or version of .block.

We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

Quick pointers

  • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
  • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
  • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
  • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
  • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

(Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

+ + \ No newline at end of file diff --git a/docs/1.13/discover/index.html b/docs/1.13/discover/index.html index 67031e3ebc..87b3f22ab6 100644 --- a/docs/1.13/discover/index.html +++ b/docs/1.13/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
-
Version: 1.13

Getting started with Richie

If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

Login/password are admin/admin. The database is regularly flushed.

Architecture

Richie is a container-native application but can also be installed +

Version: 1.13

Getting started with Richie

If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

Login/password are admin/admin. The database is regularly flushed.

Architecture

Richie is a container-native application but can also be installed on your machine.

For development, the project is defined using a docker-compose file and consists of 4 services:

  • db: the Postgresql database,
  • elasticsearch: the search engine,
  • app: the actual DjangoCMS project with all our application code,
  • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -21,7 +21,7 @@ migration-related issues.

Now that your Docker services are ready to be used, start the full CMS by running:

$ make run

Adding content

Once the CMS is up and running, you can create a superuser account:

$ make superuser

You can create a basic demo site by running:

$ make demo-site

Note that if you don't create the demo site and start from a blank CMS, you will get some errors requesting you to create some required root pages. So it is easier as a first approach to test the CMS with the demo site.

You should be able to view the site at localhost:8070

- - + + \ No newline at end of file diff --git a/docs/1.13/django-react-interop/index.html b/docs/1.13/django-react-interop/index.html index bd071d18c1..953febc15b 100644 --- a/docs/1.13/django-react-interop/index.html +++ b/docs/1.13/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
-
Version: 1.13

Connecting React components with Django

richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

Rendering components

We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

Additionally, components including internationalized data or strings need to know which locale to use. All uses of this richie-react class can be paired with a data-attribute specifying the locale. If not defined, en is used as a default.

Example

Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

<div
class="richie-react richie-react--featured-courses"
data-locale="fr-ca"
></div>

When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

Passing properties to components

Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

Example

Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

<div
class="richie-react richie-react--featured-courses"
data-locale="fr-ca"
data-props='{"categories": ["sociology", "anthropology"]}'
></div>

When the component is rendered, it will be passed a categories prop with the relevant categories.

Built-in components

Here are the React component that Richie comes with and uses out of the box.

<RootSearchSuggestField />

Renders a course search bar, like the one that appears in the default Search page.

Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

Props:

  • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
  • context [optional] — see context.

<Search />

Renders the full-page course search engine interface, including the search bar, search results, and filters pane.

NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

Props:

  • pageTitle [required] — title for the page, will be used inside the <h1> in the rendered component.
  • context [required] — see context.

<UserLogin />

Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

Props:

  • loginUrl [required] — the URL where the user is sent when they click on "Log in";
  • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
  • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

Context

All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

Here is the expected shape for this object:

{
assets: {
// SVG sprite used throughout Richie
icons: "/path/to/icons/sprite.svg"
}
}

Note that it might be expanded in further versions of Richie.

- - +
Version: 1.13

Connecting React components with Django

richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

Rendering components

We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

Additionally, components including internationalized data or strings need to know which locale to use. All uses of this richie-react class can be paired with a data-attribute specifying the locale. If not defined, en is used as a default.

Example

Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

<div
class="richie-react richie-react--featured-courses"
data-locale="fr-ca"
></div>

When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

Passing properties to components

Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

Example

Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

<div
class="richie-react richie-react--featured-courses"
data-locale="fr-ca"
data-props='{"categories": ["sociology", "anthropology"]}'
></div>

When the component is rendered, it will be passed a categories prop with the relevant categories.

Built-in components

Here are the React component that Richie comes with and uses out of the box.

<RootSearchSuggestField />

Renders a course search bar, like the one that appears in the default Search page.

Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

Props:

  • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
  • context [optional] — see context.

<Search />

Renders the full-page course search engine interface, including the search bar, search results, and filters pane.

NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

Props:

  • pageTitle [required] — title for the page, will be used inside the <h1> in the rendered component.
  • context [required] — see context.

<UserLogin />

Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

Props:

  • loginUrl [required] — the URL where the user is sent when they click on "Log in";
  • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
  • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

Context

All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

Here is the expected shape for this object:

{
assets: {
// SVG sprite used throughout Richie
icons: "/path/to/icons/sprite.svg"
}
}

Note that it might be expanded in further versions of Richie.

+ + \ No newline at end of file diff --git a/docs/1.13/docker-development/index.html b/docs/1.13/docker-development/index.html index 271d901960..9021a6c0dc 100644 --- a/docs/1.13/docker-development/index.html +++ b/docs/1.13/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
-
Version: 1.13

Developing Richie with Docker

Now that you have Richie up and running, you can start working with it.

Settings

Settings are defined using Django +

Version: 1.13

Developing Richie with Docker

Now that you have Richie up and running, you can start working with it.

Settings

Settings are defined using Django Configurations for different environments:

  • Development: settings for development on developers' local environment,
  • Test: settings used to run our test suite,
  • ContinousIntegration: settings used on the continuous integration platform,
  • Feature: settings for deployment of each developers' feature branches,
  • Staging: settings for deployment to the staging environment,
  • PreProduction: settings for deployment to the pre-production environment,
  • Production: settings for deployment to the production environment.

The Development environment is defined as the default environment.

Front-end tools

If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

# Start the Sass watcher
$ make watch-sass

# In a new terminal or session, start the TypeScript watcher
$ make watch-ts

Container control

You can stop/start/restart a container:

$ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

or stop/start/restart all containers in one command:

$ docker-compose [stop|start|restart]

Debugging

You can easily see the latest logs for a container:

$ docker-compose logs [app|postgresql|mysql|elasticsearch]

Or follow the stream of logs:

$ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

If you need to debug a running container, you can open a Linux shell with the @@ -28,7 +28,7 @@ file

Cleanup

If you work on the Docker configuration and make repeated modifications, remember to periodically clean the unused docker images and containers by running:

$ docker image prune
$ docker container prune

Troubleshooting

ElasticSearch service is always down

If your elasticsearch container fails at booting, checkout the logs via:

$ docker-compose logs elasticsearch

You may see entries similar to:

[1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

In this case, increase virtual memory as follows (UNIX systems):

$ sudo sysctl -w vm/max_map_count=262144
- - + + \ No newline at end of file diff --git a/docs/1.13/native-installation/index.html b/docs/1.13/native-installation/index.html index 61e86aaa2a..cbd919fe3b 100644 --- a/docs/1.13/native-installation/index.html +++ b/docs/1.13/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
-
Version: 1.13

Installing Richie on your machine

This document aims to list all needed steps to have a working Richie +

Version: 1.13

Installing Richie on your machine

This document aims to list all needed steps to have a working Richie installation on your laptop.

A better approach is to use Docker as explained in our guide for container-native instructions.

Installing a fresh server

Version

You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

npm run sass

Run server

Make sure your database is up-to-date before running the application the first time and after each modification to your models:

python sandbox/manage.py migrate

You can create a superuser account:

python sandbox/manage.py createsuperuser

Run the tests

python sandbox/manage.py test

You should now be able to start Django and view the site at localhost:8000

python sandbox/manage.py runserver
- - + + \ No newline at end of file diff --git a/docs/1.14/building-the-frontend/index.html b/docs/1.14/building-the-frontend/index.html index 4994cd8f58..fcc1a793fd 100644 --- a/docs/1.14/building-the-frontend/index.html +++ b/docs/1.14/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
-
Version: 1.14

Building Richie's frontend in your own project

Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

Installing richie-education

If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

mkdir -p src/frontend

Then, you need to bootstrap your own frontend project in this new directory.

cd src/frontend
yarn init

With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

yarn add richie-education

In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

"dependencies": {
"richie-education": "1.12.0"
},

Building the Javascript bundle

You are now ready to run your own frontend build. We'll just be using webpack directly.

yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

Here is everything that is happening:

  • yarn webpack — run the webpack CLI;
  • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
  • --output-path ./build — make sure we get our output where we need it to be;
  • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

You can now run your build to change frontend settings or override frontend components with your own.

Building the CSS

If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

mkdir -p src/frontend/scss
touch src/frontend/scss/_mains.scss

Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

@import "richie-education/scss/main";

You are now ready to run the CSS build:

cd src/frontend
yarn build-sass

This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

- - +
Version: 1.14

Building Richie's frontend in your own project

Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

Installing richie-education

If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

mkdir -p src/frontend

Then, you need to bootstrap your own frontend project in this new directory.

cd src/frontend
yarn init

With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

yarn add richie-education

In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

"dependencies": {
"richie-education": "1.12.0"
},

Building the Javascript bundle

You are now ready to run your own frontend build. We'll just be using webpack directly.

yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

Here is everything that is happening:

  • yarn webpack — run the webpack CLI;
  • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
  • --output-path ./build — make sure we get our output where we need it to be;
  • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

You can now run your build to change frontend settings or override frontend components with your own.

Building the CSS

If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

mkdir -p src/frontend/scss
touch src/frontend/scss/_mains.scss

Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

@import "richie-education/scss/main";

You are now ready to run the CSS build:

cd src/frontend
yarn build-sass

This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

+ + \ No newline at end of file diff --git a/docs/1.14/contributing-guide/index.html b/docs/1.14/contributing-guide/index.html index 3a37e354dc..9b1e2e9546 100644 --- a/docs/1.14/contributing-guide/index.html +++ b/docs/1.14/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
-
Version: 1.14

Contributing guide

This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

We try to raise our code quality standards and expect contributors to follow the recommandations +

Version: 1.14

Contributing guide

This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

Checking your code

We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

$ make lint-back

We use strict eslint and prettier to check the validity of our frontend code:

$ make lint-front

Running tests

On the backend, we use pytest to run our test suite:

$ make test-back

On the frontend, we use karma to run our test suite:

$ make test-front

Running migrations

The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

$ make migrate

Handling new dependencies

Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

$ make bootstrap

Going further

To see all available commands, run:

$ make

We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

bin
├── exec
├── pylint
├── pytest
└── run

More details and tips & tricks can be found in our development with Docker documentation

- - + + \ No newline at end of file diff --git a/docs/1.14/css-guidelines/index.html b/docs/1.14/css-guidelines/index.html index 17a89f50ac..f028ee5316 100644 --- a/docs/1.14/css-guidelines/index.html +++ b/docs/1.14/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
-
Version: 1.14

CSS Guidelines

The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

File structuration

Rules should be placed where their purpose is most apparent, and where they are easiest to find.

Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

Code structuration

In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

Following the BEM naming convention, we will write our classes as follows :

.block {}
.block__element {}
.block--modifier {}
  • .block represents the higher level of an abstraction or component.
  • .block__element represents a descendent of .block that helps form .block as a whole.
  • .block--modifier represents a different state or version of .block.

We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

Quick pointers

  • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
  • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
  • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
  • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
  • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

(Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

- - +
Version: 1.14

CSS Guidelines

The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

File structuration

Rules should be placed where their purpose is most apparent, and where they are easiest to find.

Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

Code structuration

In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

Following the BEM naming convention, we will write our classes as follows :

.block {}
.block__element {}
.block--modifier {}
  • .block represents the higher level of an abstraction or component.
  • .block__element represents a descendent of .block that helps form .block as a whole.
  • .block--modifier represents a different state or version of .block.

We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

Quick pointers

  • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
  • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
  • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
  • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
  • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

(Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

+ + \ No newline at end of file diff --git a/docs/1.14/discover/index.html b/docs/1.14/discover/index.html index 9b95ac5d1b..1198e27ec3 100644 --- a/docs/1.14/discover/index.html +++ b/docs/1.14/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
-
Version: 1.14

Getting started with Richie

If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

Login/password are admin/admin. The database is regularly flushed.

Architecture

Richie is a container-native application but can also be installed +

Version: 1.14

Getting started with Richie

If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

Login/password are admin/admin. The database is regularly flushed.

Architecture

Richie is a container-native application but can also be installed on your machine.

For development, the project is defined using a docker-compose file and consists of 4 services:

  • db: the Postgresql database,
  • elasticsearch: the search engine,
  • app: the actual DjangoCMS project with all our application code,
  • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -21,7 +21,7 @@ migration-related issues.

Now that your Docker services are ready to be used, start the full CMS by running:

$ make run

Adding content

Once the CMS is up and running, you can create a superuser account:

$ make superuser

You can create a basic demo site by running:

$ make demo-site

Note that if you don't create the demo site and start from a blank CMS, you will get some errors requesting you to create some required root pages. So it is easier as a first approach to test the CMS with the demo site.

You should be able to view the site at localhost:8070

- - + + \ No newline at end of file diff --git a/docs/1.14/django-react-interop/index.html b/docs/1.14/django-react-interop/index.html index 8874edd73a..0a936961cd 100644 --- a/docs/1.14/django-react-interop/index.html +++ b/docs/1.14/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
-
Version: 1.14

Connecting React components with Django

richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

Rendering components

We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

They use the BCP47/RFC5646 format.

<html lang="en-US">

Example

Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

<div
class="richie-react richie-react--featured-courses"
></div>

When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

Passing properties to components

Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

Example

Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

<div
class="richie-react richie-react--featured-courses"
data-props='{"categories": ["sociology", "anthropology"]}'
></div>

When the component is rendered, it will be passed a categories prop with the relevant categories.

Built-in components

Here are the React component that Richie comes with and uses out of the box.

<RootSearchSuggestField />

Renders a course search bar, like the one that appears in the default Search page.

Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

Props:

  • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
  • context [optional] — see context.

<Search />

Renders the full-page course search engine interface, including the search bar, search results, and filters pane.

NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

Props:

  • pageTitle [required] — title for the page, will be used inside the <h1> in the rendered component.
  • context [required] — see context.

<UserLogin />

Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

Props:

  • loginUrl [required] — the URL where the user is sent when they click on "Log in";
  • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
  • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

Context

All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

Here is the expected shape for this object:

{
assets: {
// SVG sprite used throughout Richie
icons: "/path/to/icons/sprite.svg"
}
}

Note that it might be expanded in further versions of Richie.

- - +
Version: 1.14

Connecting React components with Django

richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

Rendering components

We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

They use the BCP47/RFC5646 format.

<html lang="en-US">

Example

Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

<div
class="richie-react richie-react--featured-courses"
></div>

When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

Passing properties to components

Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

Example

Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

<div
class="richie-react richie-react--featured-courses"
data-props='{"categories": ["sociology", "anthropology"]}'
></div>

When the component is rendered, it will be passed a categories prop with the relevant categories.

Built-in components

Here are the React component that Richie comes with and uses out of the box.

<RootSearchSuggestField />

Renders a course search bar, like the one that appears in the default Search page.

Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

Props:

  • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
  • context [optional] — see context.

<Search />

Renders the full-page course search engine interface, including the search bar, search results, and filters pane.

NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

Props:

  • pageTitle [required] — title for the page, will be used inside the <h1> in the rendered component.
  • context [required] — see context.

<UserLogin />

Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

Props:

  • loginUrl [required] — the URL where the user is sent when they click on "Log in";
  • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
  • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

Context

All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

Here is the expected shape for this object:

{
assets: {
// SVG sprite used throughout Richie
icons: "/path/to/icons/sprite.svg"
}
}

Note that it might be expanded in further versions of Richie.

+ + \ No newline at end of file diff --git a/docs/1.14/docker-development/index.html b/docs/1.14/docker-development/index.html index 7bee75cbcf..d6f289abdf 100644 --- a/docs/1.14/docker-development/index.html +++ b/docs/1.14/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
-
Version: 1.14

Developing Richie with Docker

Now that you have Richie up and running, you can start working with it.

Settings

Settings are defined using Django +

Version: 1.14

Developing Richie with Docker

Now that you have Richie up and running, you can start working with it.

Settings

Settings are defined using Django Configurations for different environments:

  • Development: settings for development on developers' local environment,
  • Test: settings used to run our test suite,
  • ContinousIntegration: settings used on the continuous integration platform,
  • Feature: settings for deployment of each developers' feature branches,
  • Staging: settings for deployment to the staging environment,
  • PreProduction: settings for deployment to the pre-production environment,
  • Production: settings for deployment to the production environment.

The Development environment is defined as the default environment.

Front-end tools

If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

# Start the Sass watcher
$ make watch-sass

# In a new terminal or session, start the TypeScript watcher
$ make watch-ts

Container control

You can stop/start/restart a container:

$ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

or stop/start/restart all containers in one command:

$ docker-compose [stop|start|restart]

Debugging

You can easily see the latest logs for a container:

$ docker-compose logs [app|postgresql|mysql|elasticsearch]

Or follow the stream of logs:

$ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

If you need to debug a running container, you can open a Linux shell with the @@ -28,7 +28,7 @@ file

Cleanup

If you work on the Docker configuration and make repeated modifications, remember to periodically clean the unused docker images and containers by running:

$ docker image prune
$ docker container prune

Troubleshooting

ElasticSearch service is always down

If your elasticsearch container fails at booting, checkout the logs via:

$ docker-compose logs elasticsearch

You may see entries similar to:

[1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

In this case, increase virtual memory as follows (UNIX systems):

$ sudo sysctl -w vm/max_map_count=262144
- - + + \ No newline at end of file diff --git a/docs/1.14/native-installation/index.html b/docs/1.14/native-installation/index.html index c267418a93..84e4a7016b 100644 --- a/docs/1.14/native-installation/index.html +++ b/docs/1.14/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
-
Version: 1.14

Installing Richie on your machine

This document aims to list all needed steps to have a working Richie +

Version: 1.14

Installing Richie on your machine

This document aims to list all needed steps to have a working Richie installation on your laptop.

A better approach is to use Docker as explained in our guide for container-native instructions.

Installing a fresh server

Version

You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

npm run sass

Run server

Make sure your database is up-to-date before running the application the first time and after each modification to your models:

python sandbox/manage.py migrate

You can create a superuser account:

python sandbox/manage.py createsuperuser

Run the tests

python sandbox/manage.py test

You should now be able to start Django and view the site at localhost:8000

python sandbox/manage.py runserver
- - + + \ No newline at end of file diff --git a/docs/1.15/building-the-frontend/index.html b/docs/1.15/building-the-frontend/index.html index bcdf763114..07fc403fee 100644 --- a/docs/1.15/building-the-frontend/index.html +++ b/docs/1.15/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
-
Version: 1.15

Building Richie's frontend in your own project

Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

Installing richie-education

If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

mkdir -p src/frontend

Then, you need to bootstrap your own frontend project in this new directory.

cd src/frontend
yarn init

With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

yarn add richie-education

In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

"dependencies": {
"richie-education": "1.12.0"
},

Building the Javascript bundle

You are now ready to run your own frontend build. We'll just be using webpack directly.

yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

Here is everything that is happening:

  • yarn webpack — run the webpack CLI;
  • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
  • --output-path ./build — make sure we get our output where we need it to be;
  • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

You can now run your build to change frontend settings or override frontend components with your own.

Building the CSS

If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

mkdir -p src/frontend/scss
touch src/frontend/scss/_mains.scss

Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

@import "richie-education/scss/main";

You are now ready to run the CSS build:

cd src/frontend
yarn build-sass

This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

- - +
Version: 1.15

Building Richie's frontend in your own project

Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

Installing richie-education

If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

mkdir -p src/frontend

Then, you need to bootstrap your own frontend project in this new directory.

cd src/frontend
yarn init

With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

yarn add richie-education

In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

"dependencies": {
"richie-education": "1.12.0"
},

Building the Javascript bundle

You are now ready to run your own frontend build. We'll just be using webpack directly.

yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

Here is everything that is happening:

  • yarn webpack — run the webpack CLI;
  • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
  • --output-path ./build — make sure we get our output where we need it to be;
  • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

You can now run your build to change frontend settings or override frontend components with your own.

Building the CSS

If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

mkdir -p src/frontend/scss
touch src/frontend/scss/_mains.scss

Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

@import "richie-education/scss/main";

You are now ready to run the CSS build:

cd src/frontend
yarn build-sass

This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

+ + \ No newline at end of file diff --git a/docs/1.15/contributing-guide/index.html b/docs/1.15/contributing-guide/index.html index d82c2e93ca..f39b4f7d4b 100644 --- a/docs/1.15/contributing-guide/index.html +++ b/docs/1.15/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
-
Version: 1.15

Contributing guide

This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

We try to raise our code quality standards and expect contributors to follow the recommandations +

Version: 1.15

Contributing guide

This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

Checking your code

We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

$ make lint-back

We use strict eslint and prettier to check the validity of our frontend code:

$ make lint-front

Running tests

On the backend, we use pytest to run our test suite:

$ make test-back

On the frontend, we use karma to run our test suite:

$ make test-front

Running migrations

The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

$ make migrate

Handling new dependencies

Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

$ make bootstrap

Going further

To see all available commands, run:

$ make

We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

bin
├── exec
├── pylint
├── pytest
└── run

More details and tips & tricks can be found in our development with Docker documentation

- - + + \ No newline at end of file diff --git a/docs/1.15/css-guidelines/index.html b/docs/1.15/css-guidelines/index.html index beef8547a7..d65407de7b 100644 --- a/docs/1.15/css-guidelines/index.html +++ b/docs/1.15/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
-
Version: 1.15

CSS Guidelines

The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

File structuration

Rules should be placed where their purpose is most apparent, and where they are easiest to find.

Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

Code structuration

In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

Following the BEM naming convention, we will write our classes as follows :

.block {}
.block__element {}
.block--modifier {}
  • .block represents the higher level of an abstraction or component.
  • .block__element represents a descendent of .block that helps form .block as a whole.
  • .block--modifier represents a different state or version of .block.

We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

Quick pointers

  • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
  • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
  • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
  • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
  • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

(Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

- - +
Version: 1.15

CSS Guidelines

The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

File structuration

Rules should be placed where their purpose is most apparent, and where they are easiest to find.

Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

Code structuration

In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

Following the BEM naming convention, we will write our classes as follows :

.block {}
.block__element {}
.block--modifier {}
  • .block represents the higher level of an abstraction or component.
  • .block__element represents a descendent of .block that helps form .block as a whole.
  • .block--modifier represents a different state or version of .block.

We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

Quick pointers

  • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
  • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
  • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
  • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
  • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

(Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

+ + \ No newline at end of file diff --git a/docs/1.15/discover/index.html b/docs/1.15/discover/index.html index 26903015f8..c5281032db 100644 --- a/docs/1.15/discover/index.html +++ b/docs/1.15/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
-
Version: 1.15

Getting started with Richie

If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

Login/password are admin/admin. The database is regularly flushed.

Architecture

Richie is a container-native application but can also be installed +

Version: 1.15

Getting started with Richie

If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

Login/password are admin/admin. The database is regularly flushed.

Architecture

Richie is a container-native application but can also be installed on your machine.

For development, the project is defined using a docker-compose file and consists of 4 services:

  • db: the Postgresql database,
  • elasticsearch: the search engine,
  • app: the actual DjangoCMS project with all our application code,
  • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -21,7 +21,7 @@ migration-related issues.

Now that your Docker services are ready to be used, start the full CMS by running:

$ make run

Adding content

Once the CMS is up and running, you can create a superuser account:

$ make superuser

You can create a basic demo site by running:

$ make demo-site

Note that if you don't create the demo site and start from a blank CMS, you will get some errors requesting you to create some required root pages. So it is easier as a first approach to test the CMS with the demo site.

You should be able to view the site at localhost:8070

- - + + \ No newline at end of file diff --git a/docs/1.15/django-react-interop/index.html b/docs/1.15/django-react-interop/index.html index 8309d2da63..21a202a535 100644 --- a/docs/1.15/django-react-interop/index.html +++ b/docs/1.15/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
-
Version: 1.15

Connecting React components with Django

richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

Rendering components

We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

They use the BCP47/RFC5646 format.

<html lang="en-US">

Example

Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

<div
class="richie-react richie-react--featured-courses"
></div>

When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

Passing properties to components

Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

Example

Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

<div
class="richie-react richie-react--featured-courses"
data-props='{"categories": ["sociology", "anthropology"]}'
></div>

When the component is rendered, it will be passed a categories prop with the relevant categories.

Built-in components

Here are the React component that Richie comes with and uses out of the box.

<RootSearchSuggestField />

Renders a course search bar, like the one that appears in the default Search page.

Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

Props:

  • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
  • context [optional] — see context.

<Search />

Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

Props:

  • context [required] — see context.

<SearchSuggestField />

Renders the course search bar that interacts directly with <Search />.

It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

Props:

  • context [optional] — see context.

<UserLogin />

Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

Props:

  • loginUrl [required] — the URL where the user is sent when they click on "Log in";
  • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
  • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

Context

All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

Here is the expected shape for this object:

{
assets: {
// SVG sprite used throughout Richie
icons: "/path/to/icons/sprite.svg"
}
}

Note that it might be expanded in further versions of Richie.

- - +
Version: 1.15

Connecting React components with Django

richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

Rendering components

We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

They use the BCP47/RFC5646 format.

<html lang="en-US">

Example

Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

<div
class="richie-react richie-react--featured-courses"
></div>

When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

Passing properties to components

Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

Example

Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

<div
class="richie-react richie-react--featured-courses"
data-props='{"categories": ["sociology", "anthropology"]}'
></div>

When the component is rendered, it will be passed a categories prop with the relevant categories.

Built-in components

Here are the React component that Richie comes with and uses out of the box.

<RootSearchSuggestField />

Renders a course search bar, like the one that appears in the default Search page.

Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

Props:

  • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
  • context [optional] — see context.

<Search />

Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

Props:

  • context [required] — see context.

<SearchSuggestField />

Renders the course search bar that interacts directly with <Search />.

It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

Props:

  • context [optional] — see context.

<UserLogin />

Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

Props:

  • loginUrl [required] — the URL where the user is sent when they click on "Log in";
  • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
  • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

Context

All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

Here is the expected shape for this object:

{
assets: {
// SVG sprite used throughout Richie
icons: "/path/to/icons/sprite.svg"
}
}

Note that it might be expanded in further versions of Richie.

+ + \ No newline at end of file diff --git a/docs/1.15/docker-development/index.html b/docs/1.15/docker-development/index.html index 7f05a7205f..3b1836732c 100644 --- a/docs/1.15/docker-development/index.html +++ b/docs/1.15/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
-
Version: 1.15

Developing Richie with Docker

Now that you have Richie up and running, you can start working with it.

Settings

Settings are defined using Django +

Version: 1.15

Developing Richie with Docker

Now that you have Richie up and running, you can start working with it.

Settings

Settings are defined using Django Configurations for different environments:

  • Development: settings for development on developers' local environment,
  • Test: settings used to run our test suite,
  • ContinousIntegration: settings used on the continuous integration platform,
  • Feature: settings for deployment of each developers' feature branches,
  • Staging: settings for deployment to the staging environment,
  • PreProduction: settings for deployment to the pre-production environment,
  • Production: settings for deployment to the production environment.

The Development environment is defined as the default environment.

Front-end tools

If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

# Start the Sass watcher
$ make watch-sass

# In a new terminal or session, start the TypeScript watcher
$ make watch-ts

Container control

You can stop/start/restart a container:

$ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

or stop/start/restart all containers in one command:

$ docker-compose [stop|start|restart]

Debugging

You can easily see the latest logs for a container:

$ docker-compose logs [app|postgresql|mysql|elasticsearch]

Or follow the stream of logs:

$ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

If you need to debug a running container, you can open a Linux shell with the @@ -28,7 +28,7 @@ file

Cleanup

If you work on the Docker configuration and make repeated modifications, remember to periodically clean the unused docker images and containers by running:

$ docker image prune
$ docker container prune

Troubleshooting

ElasticSearch service is always down

If your elasticsearch container fails at booting, checkout the logs via:

$ docker-compose logs elasticsearch

You may see entries similar to:

[1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

In this case, increase virtual memory as follows (UNIX systems):

$ sudo sysctl -w vm/max_map_count=262144
- - + + \ No newline at end of file diff --git a/docs/1.15/native-installation/index.html b/docs/1.15/native-installation/index.html index 9b1d8d3cff..67c1aa96e1 100644 --- a/docs/1.15/native-installation/index.html +++ b/docs/1.15/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
-
Version: 1.15

Installing Richie on your machine

This document aims to list all needed steps to have a working Richie +

Version: 1.15

Installing Richie on your machine

This document aims to list all needed steps to have a working Richie installation on your laptop.

A better approach is to use Docker as explained in our guide for container-native instructions.

Installing a fresh server

Version

You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

npm run sass

Run server

Make sure your database is up-to-date before running the application the first time and after each modification to your models:

python sandbox/manage.py migrate

You can create a superuser account:

python sandbox/manage.py createsuperuser

Run the tests

python sandbox/manage.py test

You should now be able to start Django and view the site at localhost:8000

python sandbox/manage.py runserver
- - + + \ No newline at end of file diff --git a/docs/1.16/accessibility-testing/index.html b/docs/1.16/accessibility-testing/index.html index f8db7574c6..97fe8e3ce9 100644 --- a/docs/1.16/accessibility-testing/index.html +++ b/docs/1.16/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
-
Version: 1.16

Automated accessibility checks

Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

Testing environment setup

Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

cd tests_e2e
yarn install

This should install everything you need.

Running the tests

There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

yarn cypress run

You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

yarn cypress open

This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

Documentation reference

- - +
Version: 1.16

Automated accessibility checks

Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

Testing environment setup

Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

cd tests_e2e
yarn install

This should install everything you need.

Running the tests

There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

yarn cypress run

You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

yarn cypress open

This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

Documentation reference

+ + \ No newline at end of file diff --git a/docs/1.16/building-the-frontend/index.html b/docs/1.16/building-the-frontend/index.html index 12ee4ce54a..e5dcfe224d 100644 --- a/docs/1.16/building-the-frontend/index.html +++ b/docs/1.16/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
-
Version: 1.16

Building Richie's frontend in your own project

Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

Installing richie-education

If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

mkdir -p src/frontend

Then, you need to bootstrap your own frontend project in this new directory.

cd src/frontend
yarn init

With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

yarn add richie-education

In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

"dependencies": {
"richie-education": "1.12.0"
},

Building the Javascript bundle

You are now ready to run your own frontend build. We'll just be using webpack directly.

yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

Here is everything that is happening:

  • yarn webpack — run the webpack CLI;
  • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
  • --output-path ./build — make sure we get our output where we need it to be;
  • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

You can now run your build to change frontend settings or override frontend components with your own.

Building the CSS

If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

mkdir -p src/frontend/scss
touch src/frontend/scss/_mains.scss

Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

@import "richie-education/scss/main";

You are now ready to run the CSS build:

cd src/frontend
yarn build-sass

This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

- - +
Version: 1.16

Building Richie's frontend in your own project

Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

Installing richie-education

If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

mkdir -p src/frontend

Then, you need to bootstrap your own frontend project in this new directory.

cd src/frontend
yarn init

With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

yarn add richie-education

In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

"dependencies": {
"richie-education": "1.12.0"
},

Building the Javascript bundle

You are now ready to run your own frontend build. We'll just be using webpack directly.

yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

Here is everything that is happening:

  • yarn webpack — run the webpack CLI;
  • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
  • --output-path ./build — make sure we get our output where we need it to be;
  • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

You can now run your build to change frontend settings or override frontend components with your own.

Building the CSS

If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

mkdir -p src/frontend/scss
touch src/frontend/scss/_mains.scss

Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

@import "richie-education/scss/main";

You are now ready to run the CSS build:

cd src/frontend
yarn build-sass

This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

+ + \ No newline at end of file diff --git a/docs/1.16/contributing-guide/index.html b/docs/1.16/contributing-guide/index.html index 55f4b0c378..733153216e 100644 --- a/docs/1.16/contributing-guide/index.html +++ b/docs/1.16/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
-
Version: 1.16

Contributing guide

This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

We try to raise our code quality standards and expect contributors to follow the recommandations +

Version: 1.16

Contributing guide

This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

Checking your code

We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

$ make lint-back

We use strict eslint and prettier to check the validity of our frontend code:

$ make lint-front

Running tests

On the backend, we use pytest to run our test suite:

$ make test-back

On the frontend, we use karma to run our test suite:

$ make test-front

Running migrations

The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

$ make migrate

Handling new dependencies

Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

$ make bootstrap

Going further

To see all available commands, run:

$ make

We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

bin
├── exec
├── pylint
├── pytest
└── run

More details and tips & tricks can be found in our development with Docker documentation

- - + + \ No newline at end of file diff --git a/docs/1.16/css-guidelines/index.html b/docs/1.16/css-guidelines/index.html index 037cfed9ea..6accaa5e6c 100644 --- a/docs/1.16/css-guidelines/index.html +++ b/docs/1.16/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
-
Version: 1.16

CSS Guidelines

The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

File structuration

Rules should be placed where their purpose is most apparent, and where they are easiest to find.

Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

Code structuration

In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

Following the BEM naming convention, we will write our classes as follows :

.block {}
.block__element {}
.block--modifier {}
  • .block represents the higher level of an abstraction or component.
  • .block__element represents a descendent of .block that helps form .block as a whole.
  • .block--modifier represents a different state or version of .block.

We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

Quick pointers

  • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
  • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
  • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
  • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
  • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

(Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

- - +
Version: 1.16

CSS Guidelines

The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

File structuration

Rules should be placed where their purpose is most apparent, and where they are easiest to find.

Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

Code structuration

In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

Following the BEM naming convention, we will write our classes as follows :

.block {}
.block__element {}
.block--modifier {}
  • .block represents the higher level of an abstraction or component.
  • .block__element represents a descendent of .block that helps form .block as a whole.
  • .block--modifier represents a different state or version of .block.

We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

Quick pointers

  • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
  • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
  • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
  • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
  • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

(Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

+ + \ No newline at end of file diff --git a/docs/1.16/discover/index.html b/docs/1.16/discover/index.html index 98893aa0e0..7a680f4682 100644 --- a/docs/1.16/discover/index.html +++ b/docs/1.16/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
-
Version: 1.16

Getting started with Richie

If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

Login/password are admin/admin. The database is regularly flushed.

Architecture

Richie is a container-native application but can also be installed +

Version: 1.16

Getting started with Richie

If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

Login/password are admin/admin. The database is regularly flushed.

Architecture

Richie is a container-native application but can also be installed on your machine.

For development, the project is defined using a docker-compose file and consists of 4 services:

  • db: the Postgresql database,
  • elasticsearch: the search engine,
  • app: the actual DjangoCMS project with all our application code,
  • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -21,7 +21,7 @@ migration-related issues.

Now that your Docker services are ready to be used, start the full CMS by running:

$ make run

Adding content

Once the CMS is up and running, you can create a superuser account:

$ make superuser

You can create a basic demo site by running:

$ make demo-site

Note that if you don't create the demo site and start from a blank CMS, you will get some errors requesting you to create some required root pages. So it is easier as a first approach to test the CMS with the demo site.

You should be able to view the site at localhost:8070

- - + + \ No newline at end of file diff --git a/docs/1.16/django-react-interop/index.html b/docs/1.16/django-react-interop/index.html index b81a5d3e5b..a4cd34dbc9 100644 --- a/docs/1.16/django-react-interop/index.html +++ b/docs/1.16/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
-
Version: 1.16

Connecting React components with Django

richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

Rendering components

We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

They use the BCP47/RFC5646 format.

<html lang="en-US">

Example

Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

<div
class="richie-react richie-react--featured-courses"
></div>

When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

Passing properties to components

Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

Example

Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

<div
class="richie-react richie-react--featured-courses"
data-props='{"categories": ["sociology", "anthropology"]}'
></div>

When the component is rendered, it will be passed a categories prop with the relevant categories.

Built-in components

Here are the React component that Richie comes with and uses out of the box.

<RootSearchSuggestField />

Renders a course search bar, like the one that appears in the default Search page.

Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

Props:

  • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
  • context [required] — see context.

<Search />

Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

Props:

  • context [required] — see context.

<SearchSuggestField />

Renders the course search bar that interacts directly with <Search />.

It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

Props:

  • context [required] — see context.

<UserLogin />

Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

Props:

  • loginUrl [required] — the URL where the user is sent when they click on "Log in";
  • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
  • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

Context

All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

Here is the expected shape for this object:

{
assets: {
// SVG sprite used throughout Richie
icons: "/path/to/icons/sprite.svg"
}
}

Note that it might be expanded in further versions of Richie.

- - +
Version: 1.16

Connecting React components with Django

richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

Rendering components

We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

They use the BCP47/RFC5646 format.

<html lang="en-US">

Example

Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

<div
class="richie-react richie-react--featured-courses"
></div>

When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

Passing properties to components

Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

Example

Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

<div
class="richie-react richie-react--featured-courses"
data-props='{"categories": ["sociology", "anthropology"]}'
></div>

When the component is rendered, it will be passed a categories prop with the relevant categories.

Built-in components

Here are the React component that Richie comes with and uses out of the box.

<RootSearchSuggestField />

Renders a course search bar, like the one that appears in the default Search page.

Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

Props:

  • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
  • context [required] — see context.

<Search />

Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

Props:

  • context [required] — see context.

<SearchSuggestField />

Renders the course search bar that interacts directly with <Search />.

It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

Props:

  • context [required] — see context.

<UserLogin />

Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

Props:

  • loginUrl [required] — the URL where the user is sent when they click on "Log in";
  • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
  • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

Context

All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

Here is the expected shape for this object:

{
assets: {
// SVG sprite used throughout Richie
icons: "/path/to/icons/sprite.svg"
}
}

Note that it might be expanded in further versions of Richie.

+ + \ No newline at end of file diff --git a/docs/1.16/docker-development/index.html b/docs/1.16/docker-development/index.html index d19331fb40..782760a3c4 100644 --- a/docs/1.16/docker-development/index.html +++ b/docs/1.16/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
-
Version: 1.16

Developing Richie with Docker

Now that you have Richie up and running, you can start working with it.

Settings

Settings are defined using Django +

Version: 1.16

Developing Richie with Docker

Now that you have Richie up and running, you can start working with it.

Settings

Settings are defined using Django Configurations for different environments:

  • Development: settings for development on developers' local environment,
  • Test: settings used to run our test suite,
  • ContinousIntegration: settings used on the continuous integration platform,
  • Feature: settings for deployment of each developers' feature branches,
  • Staging: settings for deployment to the staging environment,
  • PreProduction: settings for deployment to the pre-production environment,
  • Production: settings for deployment to the production environment.

The Development environment is defined as the default environment.

Front-end tools

If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

# Start the Sass watcher
$ make watch-sass

# In a new terminal or session, start the TypeScript watcher
$ make watch-ts

Container control

You can stop/start/restart a container:

$ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

or stop/start/restart all containers in one command:

$ docker-compose [stop|start|restart]

Debugging

You can easily see the latest logs for a container:

$ docker-compose logs [app|postgresql|mysql|elasticsearch]

Or follow the stream of logs:

$ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

If you need to debug a running container, you can open a Linux shell with the @@ -28,7 +28,7 @@ file

Cleanup

If you work on the Docker configuration and make repeated modifications, remember to periodically clean the unused docker images and containers by running:

$ docker image prune
$ docker container prune

Troubleshooting

ElasticSearch service is always down

If your elasticsearch container fails at booting, checkout the logs via:

$ docker-compose logs elasticsearch

You may see entries similar to:

[1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

In this case, increase virtual memory as follows (UNIX systems):

$ sudo sysctl -w vm/max_map_count=262144
- - + + \ No newline at end of file diff --git a/docs/1.16/native-installation/index.html b/docs/1.16/native-installation/index.html index 304ec2df9f..de02572a4a 100644 --- a/docs/1.16/native-installation/index.html +++ b/docs/1.16/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
-
Version: 1.16

Installing Richie on your machine

This document aims to list all needed steps to have a working Richie +

Version: 1.16

Installing Richie on your machine

This document aims to list all needed steps to have a working Richie installation on your laptop.

A better approach is to use Docker as explained in our guide for container-native instructions.

Installing a fresh server

Version

You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

npm run sass

Run server

Make sure your database is up-to-date before running the application the first time and after each modification to your models:

python sandbox/manage.py migrate

You can create a superuser account:

python sandbox/manage.py createsuperuser

Run the tests

python sandbox/manage.py test

You should now be able to start Django and view the site at localhost:8000

python sandbox/manage.py runserver
- - + + \ No newline at end of file diff --git a/docs/1.17/accessibility-testing/index.html b/docs/1.17/accessibility-testing/index.html index 81e9d57fe1..bd219ca024 100644 --- a/docs/1.17/accessibility-testing/index.html +++ b/docs/1.17/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
-
Version: 1.17

Automated accessibility checks

Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

Testing environment setup

Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

cd tests_e2e
yarn install

This should install everything you need.

Running the tests

There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

yarn cypress run

You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

yarn cypress open

This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

Documentation reference

- - +
Version: 1.17

Automated accessibility checks

Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

Testing environment setup

Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

cd tests_e2e
yarn install

This should install everything you need.

Running the tests

There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

yarn cypress run

You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

yarn cypress open

This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

Documentation reference

+ + \ No newline at end of file diff --git a/docs/1.17/building-the-frontend/index.html b/docs/1.17/building-the-frontend/index.html index 03ecd25f19..19623314c6 100644 --- a/docs/1.17/building-the-frontend/index.html +++ b/docs/1.17/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
-
Version: 1.17

Building Richie's frontend in your own project

Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

Installing richie-education

If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

mkdir -p src/frontend

Then, you need to bootstrap your own frontend project in this new directory.

cd src/frontend
yarn init

With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

yarn add richie-education

In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

"dependencies": {
"richie-education": "1.12.0"
},

Building the Javascript bundle

You are now ready to run your own frontend build. We'll just be using webpack directly.

yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

Here is everything that is happening:

  • yarn webpack — run the webpack CLI;
  • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
  • --output-path ./build — make sure we get our output where we need it to be;
  • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

You can now run your build to change frontend settings or override frontend components with your own.

Building the CSS

If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

mkdir -p src/frontend/scss
touch src/frontend/scss/_mains.scss

Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

@import "richie-education/scss/main";

You are now ready to run the CSS build:

cd src/frontend
yarn build-sass

This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

- - +
Version: 1.17

Building Richie's frontend in your own project

Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

Installing richie-education

If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

mkdir -p src/frontend

Then, you need to bootstrap your own frontend project in this new directory.

cd src/frontend
yarn init

With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

yarn add richie-education

In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

"dependencies": {
"richie-education": "1.12.0"
},

Building the Javascript bundle

You are now ready to run your own frontend build. We'll just be using webpack directly.

yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

Here is everything that is happening:

  • yarn webpack — run the webpack CLI;
  • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
  • --output-path ./build — make sure we get our output where we need it to be;
  • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

You can now run your build to change frontend settings or override frontend components with your own.

Building the CSS

If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

mkdir -p src/frontend/scss
touch src/frontend/scss/_mains.scss

Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

@import "richie-education/scss/main";

You are now ready to run the CSS build:

cd src/frontend
yarn build-sass

This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

+ + \ No newline at end of file diff --git a/docs/1.17/contributing-guide/index.html b/docs/1.17/contributing-guide/index.html index 93d2326438..4c5ae80d59 100644 --- a/docs/1.17/contributing-guide/index.html +++ b/docs/1.17/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
-
Version: 1.17

Contributing guide

This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

We try to raise our code quality standards and expect contributors to follow the recommandations +

Version: 1.17

Contributing guide

This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

Checking your code

We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

$ make lint-back

We use strict eslint and prettier to check the validity of our frontend code:

$ make lint-front

Running tests

On the backend, we use pytest to run our test suite:

$ make test-back

On the frontend, we use karma to run our test suite:

$ make test-front

Running migrations

The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

$ make migrate

Handling new dependencies

Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

$ make bootstrap

Going further

To see all available commands, run:

$ make

We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

bin
├── exec
├── pylint
├── pytest
└── run

More details and tips & tricks can be found in our development with Docker documentation

- - + + \ No newline at end of file diff --git a/docs/1.17/css-guidelines/index.html b/docs/1.17/css-guidelines/index.html index eebdae65d9..6e8b29a9f5 100644 --- a/docs/1.17/css-guidelines/index.html +++ b/docs/1.17/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
-
Version: 1.17

CSS Guidelines

The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

File structuration

Rules should be placed where their purpose is most apparent, and where they are easiest to find.

Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

Code structuration

In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

Following the BEM naming convention, we will write our classes as follows :

.block {}
.block__element {}
.block--modifier {}
  • .block represents the higher level of an abstraction or component.
  • .block__element represents a descendent of .block that helps form .block as a whole.
  • .block--modifier represents a different state or version of .block.

We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

Quick pointers

  • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
  • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
  • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
  • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
  • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

(Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

- - +
Version: 1.17

CSS Guidelines

The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

File structuration

Rules should be placed where their purpose is most apparent, and where they are easiest to find.

Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

Code structuration

In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

Following the BEM naming convention, we will write our classes as follows :

.block {}
.block__element {}
.block--modifier {}
  • .block represents the higher level of an abstraction or component.
  • .block__element represents a descendent of .block that helps form .block as a whole.
  • .block--modifier represents a different state or version of .block.

We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

Quick pointers

  • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
  • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
  • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
  • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
  • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

(Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

+ + \ No newline at end of file diff --git a/docs/1.17/discover/index.html b/docs/1.17/discover/index.html index a39ea483b2..d0fc510863 100644 --- a/docs/1.17/discover/index.html +++ b/docs/1.17/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
-
Version: 1.17

Getting started with Richie

If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

Login/password are admin/admin. The database is regularly flushed.

Architecture

Richie is a container-native application but can also be installed +

Version: 1.17

Getting started with Richie

If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

Login/password are admin/admin. The database is regularly flushed.

Architecture

Richie is a container-native application but can also be installed on your machine.

For development, the project is defined using a docker-compose file and consists of 4 services:

  • db: the Postgresql database,
  • elasticsearch: the search engine,
  • app: the actual DjangoCMS project with all our application code,
  • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -21,7 +21,7 @@ migration-related issues.

Now that your Docker services are ready to be used, start the full CMS by running:

$ make run

Adding content

Once the CMS is up and running, you can create a superuser account:

$ make superuser

You can create a basic demo site by running:

$ make demo-site

Note that if you don't create the demo site and start from a blank CMS, you will get some errors requesting you to create some required root pages. So it is easier as a first approach to test the CMS with the demo site.

You should be able to view the site at localhost:8070

- - + + \ No newline at end of file diff --git a/docs/1.17/django-react-interop/index.html b/docs/1.17/django-react-interop/index.html index d141506c50..2f86a01f76 100644 --- a/docs/1.17/django-react-interop/index.html +++ b/docs/1.17/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
-
Version: 1.17

Connecting React components with Django

richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

Rendering components

We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

They use the BCP47/RFC5646 format.

<html lang="en-US">

Example

Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

<div
class="richie-react richie-react--featured-courses"
></div>

When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

Passing properties to components

Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

Example

Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

<div
class="richie-react richie-react--featured-courses"
data-props='{"categories": ["sociology", "anthropology"]}'
></div>

When the component is rendered, it will be passed a categories prop with the relevant categories.

Built-in components

Here are the React component that Richie comes with and uses out of the box.

<RootSearchSuggestField />

Renders a course search bar, like the one that appears in the default Search page.

Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

Props:

  • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
  • context [required] — see context.

<Search />

Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

Props:

  • context [required] — see context.

<SearchSuggestField />

Renders the course search bar that interacts directly with <Search />.

It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

Props:

  • context [required] — see context.

<UserLogin />

Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

Props:

  • loginUrl [required] — the URL where the user is sent when they click on "Log in";
  • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
  • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

Context

All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

Here is the expected shape for this object:

{
assets: {
// SVG sprite used throughout Richie
icons: "/path/to/icons/sprite.svg"
}
}

Note that it might be expanded in further versions of Richie.

- - +
Version: 1.17

Connecting React components with Django

richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

Rendering components

We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

They use the BCP47/RFC5646 format.

<html lang="en-US">

Example

Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

<div
class="richie-react richie-react--featured-courses"
></div>

When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

Passing properties to components

Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

Example

Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

<div
class="richie-react richie-react--featured-courses"
data-props='{"categories": ["sociology", "anthropology"]}'
></div>

When the component is rendered, it will be passed a categories prop with the relevant categories.

Built-in components

Here are the React component that Richie comes with and uses out of the box.

<RootSearchSuggestField />

Renders a course search bar, like the one that appears in the default Search page.

Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

Props:

  • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
  • context [required] — see context.

<Search />

Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

Props:

  • context [required] — see context.

<SearchSuggestField />

Renders the course search bar that interacts directly with <Search />.

It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

Props:

  • context [required] — see context.

<UserLogin />

Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

Props:

  • loginUrl [required] — the URL where the user is sent when they click on "Log in";
  • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
  • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

Context

All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

Here is the expected shape for this object:

{
assets: {
// SVG sprite used throughout Richie
icons: "/path/to/icons/sprite.svg"
}
}

Note that it might be expanded in further versions of Richie.

+ + \ No newline at end of file diff --git a/docs/1.17/docker-development/index.html b/docs/1.17/docker-development/index.html index cec1b53db8..c8589a436d 100644 --- a/docs/1.17/docker-development/index.html +++ b/docs/1.17/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
-
Version: 1.17

Developing Richie with Docker

Now that you have Richie up and running, you can start working with it.

Settings

Settings are defined using Django +

Version: 1.17

Developing Richie with Docker

Now that you have Richie up and running, you can start working with it.

Settings

Settings are defined using Django Configurations for different environments:

  • Development: settings for development on developers' local environment,
  • Test: settings used to run our test suite,
  • ContinousIntegration: settings used on the continuous integration platform,
  • Feature: settings for deployment of each developers' feature branches,
  • Staging: settings for deployment to the staging environment,
  • PreProduction: settings for deployment to the pre-production environment,
  • Production: settings for deployment to the production environment.

The Development environment is defined as the default environment.

Front-end tools

If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

# Start the Sass watcher
$ make watch-sass

# In a new terminal or session, start the TypeScript watcher
$ make watch-ts

Container control

You can stop/start/restart a container:

$ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

or stop/start/restart all containers in one command:

$ docker-compose [stop|start|restart]

Debugging

You can easily see the latest logs for a container:

$ docker-compose logs [app|postgresql|mysql|elasticsearch]

Or follow the stream of logs:

$ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

If you need to debug a running container, you can open a Linux shell with the @@ -28,7 +28,7 @@ file

Cleanup

If you work on the Docker configuration and make repeated modifications, remember to periodically clean the unused docker images and containers by running:

$ docker image prune
$ docker container prune

Troubleshooting

ElasticSearch service is always down

If your elasticsearch container fails at booting, checkout the logs via:

$ docker-compose logs elasticsearch

You may see entries similar to:

[1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

In this case, increase virtual memory as follows (UNIX systems):

$ sudo sysctl -w vm/max_map_count=262144
- - + + \ No newline at end of file diff --git a/docs/1.17/native-installation/index.html b/docs/1.17/native-installation/index.html index 38fab6bfe5..30d0156f56 100644 --- a/docs/1.17/native-installation/index.html +++ b/docs/1.17/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
-
Version: 1.17

Installing Richie on your machine

This document aims to list all needed steps to have a working Richie +

Version: 1.17

Installing Richie on your machine

This document aims to list all needed steps to have a working Richie installation on your laptop.

A better approach is to use Docker as explained in our guide for container-native instructions.

Installing a fresh server

Version

You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

npm run sass

Run server

Make sure your database is up-to-date before running the application the first time and after each modification to your models:

python sandbox/manage.py migrate

You can create a superuser account:

python sandbox/manage.py createsuperuser

Run the tests

python sandbox/manage.py test

You should now be able to start Django and view the site at localhost:8000

python sandbox/manage.py runserver
- - + + \ No newline at end of file diff --git a/docs/2.0.0/accessibility-testing/index.html b/docs/2.0.0/accessibility-testing/index.html index 0050d65678..d72a799c32 100644 --- a/docs/2.0.0/accessibility-testing/index.html +++ b/docs/2.0.0/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
-
Version: 2.0.0

Automated accessibility checks

Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

Testing environment setup

Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

cd tests_e2e
yarn install

This should install everything you need.

Running the tests

There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

yarn cypress run

You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

yarn cypress open

This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

Documentation reference

- - +
Version: 2.0.0

Automated accessibility checks

Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

Testing environment setup

Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

cd tests_e2e
yarn install

This should install everything you need.

Running the tests

There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

yarn cypress run

You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

yarn cypress open

This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

Documentation reference

+ + \ No newline at end of file diff --git a/docs/2.0.0/building-the-frontend/index.html b/docs/2.0.0/building-the-frontend/index.html index c4358f0bbe..ff5c154e30 100644 --- a/docs/2.0.0/building-the-frontend/index.html +++ b/docs/2.0.0/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
-
Version: 2.0.0

Building Richie's frontend in your own project

Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

Installing richie-education

If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

mkdir -p src/frontend

Then, you need to bootstrap your own frontend project in this new directory.

cd src/frontend
yarn init

With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

yarn add richie-education

In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

"dependencies": {
"richie-education": "1.12.0"
},

Building the Javascript bundle

You are now ready to run your own frontend build. We'll just be using webpack directly.

yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

Here is everything that is happening:

  • yarn webpack — run the webpack CLI;
  • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
  • --output-path ./build — make sure we get our output where we need it to be;
  • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

You can now run your build to change frontend settings or override frontend components with your own.

Building the CSS

If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

mkdir -p src/frontend/scss
touch src/frontend/scss/_mains.scss

Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

@import "richie-education/scss/main";

You are now ready to run the CSS build:

cd src/frontend
yarn build-sass

This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

- - +
Version: 2.0.0

Building Richie's frontend in your own project

Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

Installing richie-education

If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

mkdir -p src/frontend

Then, you need to bootstrap your own frontend project in this new directory.

cd src/frontend
yarn init

With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

yarn add richie-education

In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

"dependencies": {
"richie-education": "1.12.0"
},

Building the Javascript bundle

You are now ready to run your own frontend build. We'll just be using webpack directly.

yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

Here is everything that is happening:

  • yarn webpack — run the webpack CLI;
  • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
  • --output-path ./build — make sure we get our output where we need it to be;
  • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

You can now run your build to change frontend settings or override frontend components with your own.

Building the CSS

If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

mkdir -p src/frontend/scss
touch src/frontend/scss/_mains.scss

Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

@import "richie-education/scss/main";

You are now ready to run the CSS build:

cd src/frontend
yarn build-sass

This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

+ + \ No newline at end of file diff --git a/docs/2.0.0/contributing-guide/index.html b/docs/2.0.0/contributing-guide/index.html index 1a2141b90a..90d916af68 100644 --- a/docs/2.0.0/contributing-guide/index.html +++ b/docs/2.0.0/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
-
Version: 2.0.0

Contributing guide

This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

We try to raise our code quality standards and expect contributors to follow the recommandations +

Version: 2.0.0

Contributing guide

This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

Checking your code

We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

$ make lint-back

We use strict eslint and prettier to check the validity of our frontend code:

$ make lint-front

Running tests

On the backend, we use pytest to run our test suite:

$ make test-back

On the frontend, we use karma to run our test suite:

$ make test-front

Running migrations

The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

$ make migrate

Handling new dependencies

Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

$ make bootstrap

Going further

To see all available commands, run:

$ make

We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

bin
├── exec
├── pylint
├── pytest
└── run

More details and tips & tricks can be found in our development with Docker documentation

- - + + \ No newline at end of file diff --git a/docs/2.0.0/css-guidelines/index.html b/docs/2.0.0/css-guidelines/index.html index da8ced3656..717e6d4b12 100644 --- a/docs/2.0.0/css-guidelines/index.html +++ b/docs/2.0.0/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
-
Version: 2.0.0

CSS Guidelines

The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

File structuration

Rules should be placed where their purpose is most apparent, and where they are easiest to find.

Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

Code structuration

In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

Following the BEM naming convention, we will write our classes as follows :

.block {}
.block__element {}
.block--modifier {}
  • .block represents the higher level of an abstraction or component.
  • .block__element represents a descendent of .block that helps form .block as a whole.
  • .block--modifier represents a different state or version of .block.

We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

Quick pointers

  • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
  • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
  • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
  • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
  • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

(Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

- - +
Version: 2.0.0

CSS Guidelines

The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

File structuration

Rules should be placed where their purpose is most apparent, and where they are easiest to find.

Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

Code structuration

In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

Following the BEM naming convention, we will write our classes as follows :

.block {}
.block__element {}
.block--modifier {}
  • .block represents the higher level of an abstraction or component.
  • .block__element represents a descendent of .block that helps form .block as a whole.
  • .block--modifier represents a different state or version of .block.

We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

Quick pointers

  • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
  • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
  • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
  • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
  • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

(Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

+ + \ No newline at end of file diff --git a/docs/2.0.0/discover/index.html b/docs/2.0.0/discover/index.html index 1f87c3887a..d0a78e2354 100644 --- a/docs/2.0.0/discover/index.html +++ b/docs/2.0.0/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
-
Version: 2.0.0

Getting started with Richie

If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

Login/password are admin/admin. The database is regularly flushed.

Architecture

Richie is a container-native application but can also be installed +

Version: 2.0.0

Getting started with Richie

If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

Login/password are admin/admin. The database is regularly flushed.

Architecture

Richie is a container-native application but can also be installed on your machine.

For development, the project is defined using a docker-compose file and consists of 4 services:

  • db: the Postgresql database,
  • elasticsearch: the search engine,
  • app: the actual DjangoCMS project with all our application code,
  • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -27,7 +27,7 @@ to bootstrap an instance.

Just start apps with make run.

Richie should respond on http://localhost:8070 and OpenEdx on http://localhost:8073.

Advanced - Connecting Richie to OpenEdx

If you want users to enroll on courses in OpenEdx directly from Richie via API calls, you should read the advanced guide to connect Richie to OpenEdx over TLS.

- - + + \ No newline at end of file diff --git a/docs/2.0.0/django-react-interop/index.html b/docs/2.0.0/django-react-interop/index.html index ed13295eab..cb16e425ad 100644 --- a/docs/2.0.0/django-react-interop/index.html +++ b/docs/2.0.0/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
-
Version: 2.0.0

Connecting React components with Django

richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

Rendering components

We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

They use the BCP47/RFC5646 format.

<html lang="en-US">

Example

Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

<div
class="richie-react richie-react--featured-courses"
></div>

When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

Passing properties to components

Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

Example

Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

<div
class="richie-react richie-react--featured-courses"
data-props='{"categories": ["sociology", "anthropology"]}'
></div>

When the component is rendered, it will be passed a categories prop with the relevant categories.

Built-in components

Here are the React component that Richie comes with and uses out of the box.

<RootSearchSuggestField />

Renders a course search bar, like the one that appears in the default Search page.

Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

Props:

  • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
  • context [required] — see context.

<Search />

Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

Props:

  • context [required] — see context.

<SearchSuggestField />

Renders the course search bar that interacts directly with <Search />.

It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

Props:

  • context [required] — see context.

<UserLogin />

Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

Props:

  • loginUrl [required] — the URL where the user is sent when they click on "Log in";
  • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
  • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

Context

All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

Here is the expected shape for this object:

{
assets: {
// SVG sprite used throughout Richie
icons: "/path/to/icons/sprite.svg"
}
}

Note that it might be expanded in further versions of Richie.

- - +
Version: 2.0.0

Connecting React components with Django

richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

Rendering components

We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

They use the BCP47/RFC5646 format.

<html lang="en-US">

Example

Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

<div
class="richie-react richie-react--featured-courses"
></div>

When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

Passing properties to components

Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

Example

Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

<div
class="richie-react richie-react--featured-courses"
data-props='{"categories": ["sociology", "anthropology"]}'
></div>

When the component is rendered, it will be passed a categories prop with the relevant categories.

Built-in components

Here are the React component that Richie comes with and uses out of the box.

<RootSearchSuggestField />

Renders a course search bar, like the one that appears in the default Search page.

Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

Props:

  • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
  • context [required] — see context.

<Search />

Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

Props:

  • context [required] — see context.

<SearchSuggestField />

Renders the course search bar that interacts directly with <Search />.

It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

Props:

  • context [required] — see context.

<UserLogin />

Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

Props:

  • loginUrl [required] — the URL where the user is sent when they click on "Log in";
  • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
  • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

Context

All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

Here is the expected shape for this object:

{
assets: {
// SVG sprite used throughout Richie
icons: "/path/to/icons/sprite.svg"
}
}

Note that it might be expanded in further versions of Richie.

+ + \ No newline at end of file diff --git a/docs/2.0.0/docker-development/index.html b/docs/2.0.0/docker-development/index.html index 52b07bad15..fee8985370 100644 --- a/docs/2.0.0/docker-development/index.html +++ b/docs/2.0.0/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
-
Version: 2.0.0

Developing Richie with Docker

Now that you have Richie up and running, you can start working with it.

Settings

Settings are defined using Django +

Version: 2.0.0

Developing Richie with Docker

Now that you have Richie up and running, you can start working with it.

Settings

Settings are defined using Django Configurations for different environments:

  • Development: settings for development on developers' local environment,
  • Test: settings used to run our test suite,
  • ContinousIntegration: settings used on the continuous integration platform,
  • Feature: settings for deployment of each developers' feature branches,
  • Staging: settings for deployment to the staging environment,
  • PreProduction: settings for deployment to the pre-production environment,
  • Production: settings for deployment to the production environment.

The Development environment is defined as the default environment.

Front-end tools

If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

# Start the Sass watcher
$ make watch-sass

# In a new terminal or session, start the TypeScript watcher
$ make watch-ts

Container control

You can stop/start/restart a container:

$ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

or stop/start/restart all containers in one command:

$ docker-compose [stop|start|restart]

Debugging

You can easily see the latest logs for a container:

$ docker-compose logs [app|postgresql|mysql|elasticsearch]

Or follow the stream of logs:

$ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

$ docker image prune
$ docker container prune

Troubleshooting

ElasticSearch service is always down

If your elasticsearch container fails at booting, checkout the logs via:

$ docker-compose logs elasticsearch

You may see entries similar to:

[1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

In this case, increase virtual memory as follows (UNIX systems):

$ sudo sysctl -w vm/max_map_count=262144

This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

vm.max_map_count=262144
- - + + \ No newline at end of file diff --git a/docs/2.0.0/frontend-overrides/index.html b/docs/2.0.0/frontend-overrides/index.html index e39498024f..1631590d4f 100644 --- a/docs/2.0.0/frontend-overrides/index.html +++ b/docs/2.0.0/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
-
Version: 2.0.0

Overriding frontend components

Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

Defining your overrides

Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

Currently, it is only possible to override components. Richie's build is only set up to handle them.

Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

{
"overrides": {
"CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
}
}

Building a component override

As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

For example, if our component to override was the following:

export interface CourseGlimpseProps {
course: Course;
context: { someProp: string };
}

export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
// Whatever happens in this component
return <p>The glimpse</p>;
};

Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

Override translation

When you create an application based on richie, you can encounter two cases about translations:

  1. You created or overrode a react component and created new translation keys
  2. You just want to override a translation in an existing richie component

Create new translation keys

Once you created your new component with its translation keys, you have to extract them with the following command:

  formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

  node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

Override an existing translation key

As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

Richie uses one file per language. Currently 4 languages supported:

  • English: filename is en-US.json
  • French: filename is fr-FR.json
  • Canadian french: filename is fr-CA.json
  • Spanish: filename is es-ES.json

For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

{
"components.UserLogin.logIn": {
"description": "Overriden text for the login button.",
"message": "Authentication"
},
}

Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

  node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

- - +
Version: 2.0.0

Overriding frontend components

Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

Defining your overrides

Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

Currently, it is only possible to override components. Richie's build is only set up to handle them.

Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

{
"overrides": {
"CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
}
}

Building a component override

As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

For example, if our component to override was the following:

export interface CourseGlimpseProps {
course: Course;
context: { someProp: string };
}

export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
// Whatever happens in this component
return <p>The glimpse</p>;
};

Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

Override translation

When you create an application based on richie, you can encounter two cases about translations:

  1. You created or overrode a react component and created new translation keys
  2. You just want to override a translation in an existing richie component

Create new translation keys

Once you created your new component with its translation keys, you have to extract them with the following command:

  formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

  node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

Override an existing translation key

As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

Richie uses one file per language. Currently 4 languages supported:

  • English: filename is en-US.json
  • French: filename is fr-FR.json
  • Canadian french: filename is fr-CA.json
  • Spanish: filename is es-ES.json

For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

{
"components.UserLogin.logIn": {
"description": "Overriden text for the login button.",
"message": "Authentication"
},
}

Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

  node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

+ + \ No newline at end of file diff --git a/docs/2.0.0/lms-connection/index.html b/docs/2.0.0/lms-connection/index.html index abf3d8cd38..1f8d11b7c1 100644 --- a/docs/2.0.0/lms-connection/index.html +++ b/docs/2.0.0/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with an LMS | Richie - - + +
-
Version: 2.0.0

Connecting Richie with an LMS

richie can be connected to one or more Learning Management Systems (LMS) like OpenEdx, Moodle +

Version: 2.0.0

Connecting Richie with an LMS

richie can be connected to one or more Learning Management Systems (LMS) like OpenEdx, Moodle or Canvas for a seamless experience between browsing the course catalog on richie and following the course itself on the LMS.

In order to connect richie with a LMS, there is an API bridge to synchronize course information and enrollments.

API bridge

The APIHandler utility acts as a proxy that routes queries to the correct LMS backend API, @@ -43,7 +43,7 @@ on https://richie.local.dev:8070 without browser warning about the certificate validity.

You need to follow these steps once. If you want to use SSL later, just use make run-ssl to run OpenEdx and Richie apps. Of course, you can still run apps without ssl by using make run.

- - + + \ No newline at end of file diff --git a/docs/2.0.0/native-installation/index.html b/docs/2.0.0/native-installation/index.html index 995c063483..bfef7a5643 100644 --- a/docs/2.0.0/native-installation/index.html +++ b/docs/2.0.0/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
-
Version: 2.0.0

Installing Richie on your machine

This document aims to list all needed steps to have a working Richie +

Version: 2.0.0

Installing Richie on your machine

This document aims to list all needed steps to have a working Richie installation on your laptop.

A better approach is to use Docker as explained in our guide for container-native instructions.

Installing a fresh server

Version

You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

npm run sass

Run server

Make sure your database is up-to-date before running the application the first time and after each modification to your models:

python sandbox/manage.py migrate

You can create a superuser account:

python sandbox/manage.py createsuperuser

Run the tests

python sandbox/manage.py test

You should now be able to start Django and view the site at localhost:8000

python sandbox/manage.py runserver
- - + + \ No newline at end of file diff --git a/docs/2.0.1/accessibility-testing/index.html b/docs/2.0.1/accessibility-testing/index.html index 7c43719a63..e59c648ac9 100644 --- a/docs/2.0.1/accessibility-testing/index.html +++ b/docs/2.0.1/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
-
Version: 2.0.1

Automated accessibility checks

Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

Testing environment setup

Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

cd tests_e2e
yarn install

This should install everything you need.

Running the tests

There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

yarn cypress run

You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

yarn cypress open

This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

Documentation reference

- - +
Version: 2.0.1

Automated accessibility checks

Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

Testing environment setup

Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

cd tests_e2e
yarn install

This should install everything you need.

Running the tests

There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

yarn cypress run

You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

yarn cypress open

This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

Documentation reference

+ + \ No newline at end of file diff --git a/docs/2.0.1/building-the-frontend/index.html b/docs/2.0.1/building-the-frontend/index.html index 09ecaa384a..3ec1670544 100644 --- a/docs/2.0.1/building-the-frontend/index.html +++ b/docs/2.0.1/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
-
Version: 2.0.1

Building Richie's frontend in your own project

Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

Installing richie-education

If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

mkdir -p src/frontend

Then, you need to bootstrap your own frontend project in this new directory.

cd src/frontend
yarn init

With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

yarn add richie-education

In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

"dependencies": {
"richie-education": "1.12.0"
},

Building the Javascript bundle

You are now ready to run your own frontend build. We'll just be using webpack directly.

yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

Here is everything that is happening:

  • yarn webpack — run the webpack CLI;
  • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
  • --output-path ./build — make sure we get our output where we need it to be;
  • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

You can now run your build to change frontend settings or override frontend components with your own.

Building the CSS

If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

mkdir -p src/frontend/scss
touch src/frontend/scss/_mains.scss

Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

@import "richie-education/scss/main";

You are now ready to run the CSS build:

cd src/frontend
yarn build-sass

This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

- - +
Version: 2.0.1

Building Richie's frontend in your own project

Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

Installing richie-education

If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

mkdir -p src/frontend

Then, you need to bootstrap your own frontend project in this new directory.

cd src/frontend
yarn init

With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

yarn add richie-education

In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

"dependencies": {
"richie-education": "1.12.0"
},

Building the Javascript bundle

You are now ready to run your own frontend build. We'll just be using webpack directly.

yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

Here is everything that is happening:

  • yarn webpack — run the webpack CLI;
  • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
  • --output-path ./build — make sure we get our output where we need it to be;
  • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

You can now run your build to change frontend settings or override frontend components with your own.

Building the CSS

If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

mkdir -p src/frontend/scss
touch src/frontend/scss/_mains.scss

Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

@import "richie-education/scss/main";

You are now ready to run the CSS build:

cd src/frontend
yarn build-sass

This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

+ + \ No newline at end of file diff --git a/docs/2.0.1/contributing-guide/index.html b/docs/2.0.1/contributing-guide/index.html index 80b068b96b..24e7b60f4c 100644 --- a/docs/2.0.1/contributing-guide/index.html +++ b/docs/2.0.1/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
-
Version: 2.0.1

Contributing guide

This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

We try to raise our code quality standards and expect contributors to follow the recommandations +

Version: 2.0.1

Contributing guide

This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

Checking your code

We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

$ make lint-back

We use strict eslint and prettier to check the validity of our frontend code:

$ make lint-front

Running tests

On the backend, we use pytest to run our test suite:

$ make test-back

On the frontend, we use karma to run our test suite:

$ make test-front

Running migrations

The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

$ make migrate

Handling new dependencies

Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

$ make bootstrap

Going further

To see all available commands, run:

$ make

We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

bin
├── exec
├── pylint
├── pytest
└── run

More details and tips & tricks can be found in our development with Docker documentation

- - + + \ No newline at end of file diff --git a/docs/2.0.1/css-guidelines/index.html b/docs/2.0.1/css-guidelines/index.html index 6e8713cac8..4c46c67eab 100644 --- a/docs/2.0.1/css-guidelines/index.html +++ b/docs/2.0.1/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
-
Version: 2.0.1

CSS Guidelines

The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

File structuration

Rules should be placed where their purpose is most apparent, and where they are easiest to find.

Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

Code structuration

In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

Following the BEM naming convention, we will write our classes as follows :

.block {}
.block__element {}
.block--modifier {}
  • .block represents the higher level of an abstraction or component.
  • .block__element represents a descendent of .block that helps form .block as a whole.
  • .block--modifier represents a different state or version of .block.

We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

Quick pointers

  • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
  • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
  • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
  • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
  • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

(Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

- - +
Version: 2.0.1

CSS Guidelines

The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

File structuration

Rules should be placed where their purpose is most apparent, and where they are easiest to find.

Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

Code structuration

In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

Following the BEM naming convention, we will write our classes as follows :

.block {}
.block__element {}
.block--modifier {}
  • .block represents the higher level of an abstraction or component.
  • .block__element represents a descendent of .block that helps form .block as a whole.
  • .block--modifier represents a different state or version of .block.

We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

Quick pointers

  • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
  • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
  • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
  • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
  • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

(Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

+ + \ No newline at end of file diff --git a/docs/2.0.1/discover/index.html b/docs/2.0.1/discover/index.html index f1c87a1d9b..b31b56e263 100644 --- a/docs/2.0.1/discover/index.html +++ b/docs/2.0.1/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
-
Version: 2.0.1

Getting started with Richie

If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

Login/password are admin/admin. The database is regularly flushed.

Architecture

Richie is a container-native application but can also be installed +

Version: 2.0.1

Getting started with Richie

If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

Login/password are admin/admin. The database is regularly flushed.

Architecture

Richie is a container-native application but can also be installed on your machine.

For development, the project is defined using a docker-compose file and consists of 4 services:

  • db: the Postgresql database,
  • elasticsearch: the search engine,
  • app: the actual DjangoCMS project with all our application code,
  • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -27,7 +27,7 @@ to bootstrap an instance.

Just start apps with make run.

Richie should respond on http://localhost:8070 and OpenEdx on http://localhost:8073.

Advanced - Connecting Richie to OpenEdx

If you want users to enroll on courses in OpenEdx directly from Richie via API calls, you should read the advanced guide to connect Richie to OpenEdx over TLS.

- - + + \ No newline at end of file diff --git a/docs/2.0.1/django-react-interop/index.html b/docs/2.0.1/django-react-interop/index.html index 259239d5b6..62c1e370ed 100644 --- a/docs/2.0.1/django-react-interop/index.html +++ b/docs/2.0.1/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
-
Version: 2.0.1

Connecting React components with Django

richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

Rendering components

We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

They use the BCP47/RFC5646 format.

<html lang="en-US">

Example

Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

<div
class="richie-react richie-react--featured-courses"
></div>

When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

Passing properties to components

Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

Example

Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

<div
class="richie-react richie-react--featured-courses"
data-props='{"categories": ["sociology", "anthropology"]}'
></div>

When the component is rendered, it will be passed a categories prop with the relevant categories.

Built-in components

Here are the React component that Richie comes with and uses out of the box.

<RootSearchSuggestField />

Renders a course search bar, like the one that appears in the default Search page.

Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

Props:

  • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
  • context [required] — see context.

<Search />

Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

Props:

  • context [required] — see context.

<SearchSuggestField />

Renders the course search bar that interacts directly with <Search />.

It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

Props:

  • context [required] — see context.

<UserLogin />

Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

Props:

  • loginUrl [required] — the URL where the user is sent when they click on "Log in";
  • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
  • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

Context

All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

Here is the expected shape for this object:

{
assets: {
// SVG sprite used throughout Richie
icons: "/path/to/icons/sprite.svg"
}
}

Note that it might be expanded in further versions of Richie.

- - +
Version: 2.0.1

Connecting React components with Django

richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

Rendering components

We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

They use the BCP47/RFC5646 format.

<html lang="en-US">

Example

Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

<div
class="richie-react richie-react--featured-courses"
></div>

When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

Passing properties to components

Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

Example

Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

<div
class="richie-react richie-react--featured-courses"
data-props='{"categories": ["sociology", "anthropology"]}'
></div>

When the component is rendered, it will be passed a categories prop with the relevant categories.

Built-in components

Here are the React component that Richie comes with and uses out of the box.

<RootSearchSuggestField />

Renders a course search bar, like the one that appears in the default Search page.

Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

Props:

  • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
  • context [required] — see context.

<Search />

Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

Props:

  • context [required] — see context.

<SearchSuggestField />

Renders the course search bar that interacts directly with <Search />.

It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

Props:

  • context [required] — see context.

<UserLogin />

Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

Props:

  • loginUrl [required] — the URL where the user is sent when they click on "Log in";
  • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
  • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

Context

All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

Here is the expected shape for this object:

{
assets: {
// SVG sprite used throughout Richie
icons: "/path/to/icons/sprite.svg"
}
}

Note that it might be expanded in further versions of Richie.

+ + \ No newline at end of file diff --git a/docs/2.0.1/docker-development/index.html b/docs/2.0.1/docker-development/index.html index 0af77ba82b..a2e729591c 100644 --- a/docs/2.0.1/docker-development/index.html +++ b/docs/2.0.1/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
-
Version: 2.0.1

Developing Richie with Docker

Now that you have Richie up and running, you can start working with it.

Settings

Settings are defined using Django +

Version: 2.0.1

Developing Richie with Docker

Now that you have Richie up and running, you can start working with it.

Settings

Settings are defined using Django Configurations for different environments:

  • Development: settings for development on developers' local environment,
  • Test: settings used to run our test suite,
  • ContinousIntegration: settings used on the continuous integration platform,
  • Feature: settings for deployment of each developers' feature branches,
  • Staging: settings for deployment to the staging environment,
  • PreProduction: settings for deployment to the pre-production environment,
  • Production: settings for deployment to the production environment.

The Development environment is defined as the default environment.

Front-end tools

If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

# Start the Sass watcher
$ make watch-sass

# In a new terminal or session, start the TypeScript watcher
$ make watch-ts

Container control

You can stop/start/restart a container:

$ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

or stop/start/restart all containers in one command:

$ docker-compose [stop|start|restart]

Debugging

You can easily see the latest logs for a container:

$ docker-compose logs [app|postgresql|mysql|elasticsearch]

Or follow the stream of logs:

$ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

$ docker image prune
$ docker container prune

Troubleshooting

ElasticSearch service is always down

If your elasticsearch container fails at booting, checkout the logs via:

$ docker-compose logs elasticsearch

You may see entries similar to:

[1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

In this case, increase virtual memory as follows (UNIX systems):

$ sudo sysctl -w vm/max_map_count=262144

This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

vm.max_map_count=262144
- - + + \ No newline at end of file diff --git a/docs/2.0.1/frontend-overrides/index.html b/docs/2.0.1/frontend-overrides/index.html index af14674411..3ceb325126 100644 --- a/docs/2.0.1/frontend-overrides/index.html +++ b/docs/2.0.1/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
-
Version: 2.0.1

Overriding frontend components

Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

Defining your overrides

Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

Currently, it is only possible to override components. Richie's build is only set up to handle them.

Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

{
"overrides": {
"CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
}
}

Building a component override

As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

For example, if our component to override was the following:

export interface CourseGlimpseProps {
course: Course;
context: { someProp: string };
}

export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
// Whatever happens in this component
return <p>The glimpse</p>;
};

Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

Override translation

When you create an application based on richie, you can encounter two cases about translations:

  1. You created or overrode a react component and created new translation keys
  2. You just want to override a translation in an existing richie component

Create new translation keys

Once you created your new component with its translation keys, you have to extract them with the following command:

  formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

  node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

Override an existing translation key

As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

Richie uses one file per language. Currently 4 languages supported:

  • English: filename is en-US.json
  • French: filename is fr-FR.json
  • Canadian french: filename is fr-CA.json
  • Spanish: filename is es-ES.json

For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

{
"components.UserLogin.logIn": {
"description": "Overriden text for the login button.",
"message": "Authentication"
},
}

Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

  node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

- - +
Version: 2.0.1

Overriding frontend components

Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

Defining your overrides

Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

Currently, it is only possible to override components. Richie's build is only set up to handle them.

Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

{
"overrides": {
"CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
}
}

Building a component override

As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

For example, if our component to override was the following:

export interface CourseGlimpseProps {
course: Course;
context: { someProp: string };
}

export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
// Whatever happens in this component
return <p>The glimpse</p>;
};

Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

Override translation

When you create an application based on richie, you can encounter two cases about translations:

  1. You created or overrode a react component and created new translation keys
  2. You just want to override a translation in an existing richie component

Create new translation keys

Once you created your new component with its translation keys, you have to extract them with the following command:

  formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

  node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

Override an existing translation key

As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

Richie uses one file per language. Currently 4 languages supported:

  • English: filename is en-US.json
  • French: filename is fr-FR.json
  • Canadian french: filename is fr-CA.json
  • Spanish: filename is es-ES.json

For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

{
"components.UserLogin.logIn": {
"description": "Overriden text for the login button.",
"message": "Authentication"
},
}

Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

  node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

+ + \ No newline at end of file diff --git a/docs/2.0.1/lms-connection/index.html b/docs/2.0.1/lms-connection/index.html index 2b9d259bea..9cd25e6124 100644 --- a/docs/2.0.1/lms-connection/index.html +++ b/docs/2.0.1/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with an LMS | Richie - - + +
-
Version: 2.0.1

Connecting Richie with an LMS

richie can be connected to one or more Learning Management Systems (LMS) like OpenEdx, Moodle +

Version: 2.0.1

Connecting Richie with an LMS

richie can be connected to one or more Learning Management Systems (LMS) like OpenEdx, Moodle or Canvas for a seamless experience between browsing the course catalog on richie and following the course itself on the LMS.

In order to connect richie with a LMS, there is an API bridge to synchronize course information and enrollments.

API bridge

The APIHandler utility acts as a proxy that routes queries to the correct LMS backend API, @@ -43,7 +43,7 @@ on https://richie.local.dev:8070 without browser warning about the certificate validity.

You need to follow these steps once. If you want to use SSL later, just use make run-ssl to run OpenEdx and Richie apps. Of course, you can still run apps without ssl by using make run.

- - + + \ No newline at end of file diff --git a/docs/2.0.1/native-installation/index.html b/docs/2.0.1/native-installation/index.html index b8939169b8..39d3669f85 100644 --- a/docs/2.0.1/native-installation/index.html +++ b/docs/2.0.1/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
-
Version: 2.0.1

Installing Richie on your machine

This document aims to list all needed steps to have a working Richie +

Version: 2.0.1

Installing Richie on your machine

This document aims to list all needed steps to have a working Richie installation on your laptop.

A better approach is to use Docker as explained in our guide for container-native instructions.

Installing a fresh server

Version

You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

npm run sass

Run server

Make sure your database is up-to-date before running the application the first time and after each modification to your models:

python sandbox/manage.py migrate

You can create a superuser account:

python sandbox/manage.py createsuperuser

Run the tests

python sandbox/manage.py test

You should now be able to start Django and view the site at localhost:8000

python sandbox/manage.py runserver
- - + + \ No newline at end of file diff --git a/docs/2.1.0/accessibility-testing/index.html b/docs/2.1.0/accessibility-testing/index.html index ca3035c321..ba668430bd 100644 --- a/docs/2.1.0/accessibility-testing/index.html +++ b/docs/2.1.0/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
-
Version: 2.1.0

Automated accessibility checks

Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

Testing environment setup

Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

cd tests_e2e
yarn install

This should install everything you need.

Running the tests

There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

yarn cypress run

You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

yarn cypress open

This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

Documentation reference

- - +
Version: 2.1.0

Automated accessibility checks

Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

Testing environment setup

Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

cd tests_e2e
yarn install

This should install everything you need.

Running the tests

There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

yarn cypress run

You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

yarn cypress open

This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

Documentation reference

+ + \ No newline at end of file diff --git a/docs/2.1.0/building-the-frontend/index.html b/docs/2.1.0/building-the-frontend/index.html index 22a1c97bc4..8ee3fff773 100644 --- a/docs/2.1.0/building-the-frontend/index.html +++ b/docs/2.1.0/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
-
Version: 2.1.0

Building Richie's frontend in your own project

Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

Installing richie-education

If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

mkdir -p src/frontend

Then, you need to bootstrap your own frontend project in this new directory.

cd src/frontend
yarn init

With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

yarn add richie-education

In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

"dependencies": {
"richie-education": "1.12.0"
},

Building the Javascript bundle

You are now ready to run your own frontend build. We'll just be using webpack directly.

yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

Here is everything that is happening:

  • yarn webpack — run the webpack CLI;
  • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
  • --output-path ./build — make sure we get our output where we need it to be;
  • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

You can now run your build to change frontend settings or override frontend components with your own.

Building the CSS

If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

mkdir -p src/frontend/scss
touch src/frontend/scss/_mains.scss

Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

@import "richie-education/scss/main";

You are now ready to run the CSS build:

cd src/frontend
yarn build-sass

This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

- - +
Version: 2.1.0

Building Richie's frontend in your own project

Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

Installing richie-education

If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

mkdir -p src/frontend

Then, you need to bootstrap your own frontend project in this new directory.

cd src/frontend
yarn init

With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

yarn add richie-education

In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

"dependencies": {
"richie-education": "1.12.0"
},

Building the Javascript bundle

You are now ready to run your own frontend build. We'll just be using webpack directly.

yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

Here is everything that is happening:

  • yarn webpack — run the webpack CLI;
  • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
  • --output-path ./build — make sure we get our output where we need it to be;
  • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

You can now run your build to change frontend settings or override frontend components with your own.

Building the CSS

If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

mkdir -p src/frontend/scss
touch src/frontend/scss/_mains.scss

Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

@import "richie-education/scss/main";

You are now ready to run the CSS build:

cd src/frontend
yarn build-sass

This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

+ + \ No newline at end of file diff --git a/docs/2.1.0/contributing-guide/index.html b/docs/2.1.0/contributing-guide/index.html index 10ac33c052..8ebaae9b30 100644 --- a/docs/2.1.0/contributing-guide/index.html +++ b/docs/2.1.0/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
-
Version: 2.1.0

Contributing guide

This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

We try to raise our code quality standards and expect contributors to follow the recommandations +

Version: 2.1.0

Contributing guide

This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

Checking your code

We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

$ make lint-back

We use strict eslint and prettier to check the validity of our frontend code:

$ make lint-front

Running tests

On the backend, we use pytest to run our test suite:

$ make test-back

On the frontend, we use karma to run our test suite:

$ make test-front

Running migrations

The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

$ make migrate

Handling new dependencies

Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

$ make bootstrap

Going further

To see all available commands, run:

$ make

We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

bin
├── exec
├── pylint
├── pytest
└── run

More details and tips & tricks can be found in our development with Docker documentation

- - + + \ No newline at end of file diff --git a/docs/2.1.0/css-guidelines/index.html b/docs/2.1.0/css-guidelines/index.html index f8a8a34730..54ff2929f1 100644 --- a/docs/2.1.0/css-guidelines/index.html +++ b/docs/2.1.0/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
-
Version: 2.1.0

CSS Guidelines

The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

File structuration

Rules should be placed where their purpose is most apparent, and where they are easiest to find.

Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

Code structuration

In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

Following the BEM naming convention, we will write our classes as follows :

.block {}
.block__element {}
.block--modifier {}
  • .block represents the higher level of an abstraction or component.
  • .block__element represents a descendent of .block that helps form .block as a whole.
  • .block--modifier represents a different state or version of .block.

We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

Quick pointers

  • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
  • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
  • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
  • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
  • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

(Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

- - +
Version: 2.1.0

CSS Guidelines

The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

File structuration

Rules should be placed where their purpose is most apparent, and where they are easiest to find.

Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

Code structuration

In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

Following the BEM naming convention, we will write our classes as follows :

.block {}
.block__element {}
.block--modifier {}
  • .block represents the higher level of an abstraction or component.
  • .block__element represents a descendent of .block that helps form .block as a whole.
  • .block--modifier represents a different state or version of .block.

We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

Quick pointers

  • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
  • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
  • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
  • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
  • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

(Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

+ + \ No newline at end of file diff --git a/docs/2.1.0/discover/index.html b/docs/2.1.0/discover/index.html index ed02bed626..140d8ae8f6 100644 --- a/docs/2.1.0/discover/index.html +++ b/docs/2.1.0/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
-
Version: 2.1.0

Getting started with Richie

If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

Login/password are admin/admin. The database is regularly flushed.

Architecture

Richie is a container-native application but can also be installed +

Version: 2.1.0

Getting started with Richie

If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

Login/password are admin/admin. The database is regularly flushed.

Architecture

Richie is a container-native application but can also be installed on your machine.

For development, the project is defined using a docker-compose file and consists of 4 services:

  • db: the Postgresql database,
  • elasticsearch: the search engine,
  • app: the actual DjangoCMS project with all our application code,
  • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -27,7 +27,7 @@ to bootstrap an instance.

Just start apps with make run.

Richie should respond on http://localhost:8070 and OpenEdx on http://localhost:8073.

Advanced - Connecting Richie to OpenEdx

If you want users to enroll on courses in OpenEdx directly from Richie via API calls, you should read the advanced guide to connect Richie to OpenEdx over TLS.

- - + + \ No newline at end of file diff --git a/docs/2.1.0/django-react-interop/index.html b/docs/2.1.0/django-react-interop/index.html index 69027145cb..1f852de9bf 100644 --- a/docs/2.1.0/django-react-interop/index.html +++ b/docs/2.1.0/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
-
Version: 2.1.0

Connecting React components with Django

richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

Rendering components

We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

They use the BCP47/RFC5646 format.

<html lang="en-US">

Example

Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

<div
class="richie-react richie-react--featured-courses"
></div>

When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

Passing properties to components

Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

Example

Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

<div
class="richie-react richie-react--featured-courses"
data-props='{"categories": ["sociology", "anthropology"]}'
></div>

When the component is rendered, it will be passed a categories prop with the relevant categories.

Built-in components

Here are the React component that Richie comes with and uses out of the box.

<RootSearchSuggestField />

Renders a course search bar, like the one that appears in the default Search page.

Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

Props:

  • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
  • context [required] — see context.

<Search />

Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

Props:

  • context [required] — see context.

<SearchSuggestField />

Renders the course search bar that interacts directly with <Search />.

It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

Props:

  • context [required] — see context.

<UserLogin />

Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

Props:

  • loginUrl [required] — the URL where the user is sent when they click on "Log in";
  • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
  • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

Context

All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

Here is the expected shape for this object:

{
assets: {
// SVG sprite used throughout Richie
icons: "/path/to/icons/sprite.svg"
}
}

Note that it might be expanded in further versions of Richie.

- - +
Version: 2.1.0

Connecting React components with Django

richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

Rendering components

We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

They use the BCP47/RFC5646 format.

<html lang="en-US">

Example

Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

<div
class="richie-react richie-react--featured-courses"
></div>

When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

Passing properties to components

Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

Example

Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

<div
class="richie-react richie-react--featured-courses"
data-props='{"categories": ["sociology", "anthropology"]}'
></div>

When the component is rendered, it will be passed a categories prop with the relevant categories.

Built-in components

Here are the React component that Richie comes with and uses out of the box.

<RootSearchSuggestField />

Renders a course search bar, like the one that appears in the default Search page.

Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

Props:

  • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
  • context [required] — see context.

<Search />

Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

Props:

  • context [required] — see context.

<SearchSuggestField />

Renders the course search bar that interacts directly with <Search />.

It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

Props:

  • context [required] — see context.

<UserLogin />

Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

Props:

  • loginUrl [required] — the URL where the user is sent when they click on "Log in";
  • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
  • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

Context

All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

Here is the expected shape for this object:

{
assets: {
// SVG sprite used throughout Richie
icons: "/path/to/icons/sprite.svg"
}
}

Note that it might be expanded in further versions of Richie.

+ + \ No newline at end of file diff --git a/docs/2.1.0/docker-development/index.html b/docs/2.1.0/docker-development/index.html index f99aec5d72..d3b7bf8a38 100644 --- a/docs/2.1.0/docker-development/index.html +++ b/docs/2.1.0/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
-
Version: 2.1.0

Developing Richie with Docker

Now that you have Richie up and running, you can start working with it.

Settings

Settings are defined using Django +

Version: 2.1.0

Developing Richie with Docker

Now that you have Richie up and running, you can start working with it.

Settings

Settings are defined using Django Configurations for different environments:

  • Development: settings for development on developers' local environment,
  • Test: settings used to run our test suite,
  • ContinousIntegration: settings used on the continuous integration platform,
  • Feature: settings for deployment of each developers' feature branches,
  • Staging: settings for deployment to the staging environment,
  • PreProduction: settings for deployment to the pre-production environment,
  • Production: settings for deployment to the production environment.

The Development environment is defined as the default environment.

Front-end tools

If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

# Start the Sass watcher
$ make watch-sass

# In a new terminal or session, start the TypeScript watcher
$ make watch-ts

Container control

You can stop/start/restart a container:

$ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

or stop/start/restart all containers in one command:

$ docker-compose [stop|start|restart]

Debugging

You can easily see the latest logs for a container:

$ docker-compose logs [app|postgresql|mysql|elasticsearch]

Or follow the stream of logs:

$ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

$ docker image prune
$ docker container prune

Troubleshooting

ElasticSearch service is always down

If your elasticsearch container fails at booting, checkout the logs via:

$ docker-compose logs elasticsearch

You may see entries similar to:

[1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

In this case, increase virtual memory as follows (UNIX systems):

$ sudo sysctl -w vm/max_map_count=262144

This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

vm.max_map_count=262144
- - + + \ No newline at end of file diff --git a/docs/2.1.0/frontend-overrides/index.html b/docs/2.1.0/frontend-overrides/index.html index 4f359cd030..f18fc04804 100644 --- a/docs/2.1.0/frontend-overrides/index.html +++ b/docs/2.1.0/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
-
Version: 2.1.0

Overriding frontend components

Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

Defining your overrides

Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

Currently, it is only possible to override components. Richie's build is only set up to handle them.

Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

{
"overrides": {
"CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
}
}

Building a component override

As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

For example, if our component to override was the following:

export interface CourseGlimpseProps {
course: Course;
context: { someProp: string };
}

export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
// Whatever happens in this component
return <p>The glimpse</p>;
};

Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

Override translation

When you create an application based on richie, you can encounter two cases about translations:

  1. You created or overrode a react component and created new translation keys
  2. You just want to override a translation in an existing richie component

Create new translation keys

Once you created your new component with its translation keys, you have to extract them with the following command:

  formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

  node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

Override an existing translation key

As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

Richie uses one file per language. Currently 4 languages supported:

  • English: filename is en-US.json
  • French: filename is fr-FR.json
  • Canadian french: filename is fr-CA.json
  • Spanish: filename is es-ES.json

For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

{
"components.UserLogin.logIn": {
"description": "Overriden text for the login button.",
"message": "Authentication"
},
}

Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

  node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

- - +
Version: 2.1.0

Overriding frontend components

Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

Defining your overrides

Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

Currently, it is only possible to override components. Richie's build is only set up to handle them.

Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

{
"overrides": {
"CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
}
}

Building a component override

As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

For example, if our component to override was the following:

export interface CourseGlimpseProps {
course: Course;
context: { someProp: string };
}

export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
// Whatever happens in this component
return <p>The glimpse</p>;
};

Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

Override translation

When you create an application based on richie, you can encounter two cases about translations:

  1. You created or overrode a react component and created new translation keys
  2. You just want to override a translation in an existing richie component

Create new translation keys

Once you created your new component with its translation keys, you have to extract them with the following command:

  formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

  node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

Override an existing translation key

As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

Richie uses one file per language. Currently 4 languages supported:

  • English: filename is en-US.json
  • French: filename is fr-FR.json
  • Canadian french: filename is fr-CA.json
  • Spanish: filename is es-ES.json

For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

{
"components.UserLogin.logIn": {
"description": "Overriden text for the login button.",
"message": "Authentication"
},
}

Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

  node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

+ + \ No newline at end of file diff --git a/docs/2.1.0/lms-connection/index.html b/docs/2.1.0/lms-connection/index.html index b901bc2cda..0981b04ca6 100644 --- a/docs/2.1.0/lms-connection/index.html +++ b/docs/2.1.0/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with an LMS | Richie - - + +
-
Version: 2.1.0

Connecting Richie with an LMS

richie can be connected to one or more Learning Management Systems (LMS) like OpenEdx, Moodle +

Version: 2.1.0

Connecting Richie with an LMS

richie can be connected to one or more Learning Management Systems (LMS) like OpenEdx, Moodle or Canvas for a seamless experience between browsing the course catalog on richie and following the course itself on the LMS.

In order to connect richie with a LMS, there is an API bridge to synchronize course information and enrollments.

API bridge

The APIHandler utility acts as a proxy that routes queries to the correct LMS backend API, @@ -43,7 +43,7 @@ on https://richie.local.dev:8070 without browser warning about the certificate validity.

You need to follow these steps once. If you want to use SSL later, just use make run-ssl to run OpenEdx and Richie apps. Of course, you can still run apps without ssl by using make run.

- - + + \ No newline at end of file diff --git a/docs/2.1.0/native-installation/index.html b/docs/2.1.0/native-installation/index.html index 883f20a8e0..a75fb6bf3c 100644 --- a/docs/2.1.0/native-installation/index.html +++ b/docs/2.1.0/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
-
Version: 2.1.0

Installing Richie on your machine

This document aims to list all needed steps to have a working Richie +

Version: 2.1.0

Installing Richie on your machine

This document aims to list all needed steps to have a working Richie installation on your laptop.

A better approach is to use Docker as explained in our guide for container-native instructions.

Installing a fresh server

Version

You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

npm run sass

Run server

Make sure your database is up-to-date before running the application the first time and after each modification to your models:

python sandbox/manage.py migrate

You can create a superuser account:

python sandbox/manage.py createsuperuser

Run the tests

python sandbox/manage.py test

You should now be able to start Django and view the site at localhost:8000

python sandbox/manage.py runserver
- - + + \ No newline at end of file diff --git a/docs/2.10.0/accessibility-testing/index.html b/docs/2.10.0/accessibility-testing/index.html index 3bf712af1d..098013fd2c 100644 --- a/docs/2.10.0/accessibility-testing/index.html +++ b/docs/2.10.0/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
-
Version: 2.10.0

Automated accessibility checks

Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

Testing environment setup

Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

cd tests_e2e
yarn install

This should install everything you need.

Running the tests

There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

yarn cypress run

You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

yarn cypress open

This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

Documentation reference

- - +
Version: 2.10.0

Automated accessibility checks

Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

Testing environment setup

Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

cd tests_e2e
yarn install

This should install everything you need.

Running the tests

There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

yarn cypress run

You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

yarn cypress open

This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

Documentation reference

+ + \ No newline at end of file diff --git a/docs/2.10.0/api/course-run-synchronization-api/index.html b/docs/2.10.0/api/course-run-synchronization-api/index.html index ba128823b5..d4b345d0d9 100644 --- a/docs/2.10.0/api/course-run-synchronization-api/index.html +++ b/docs/2.10.0/api/course-run-synchronization-api/index.html @@ -4,12 +4,12 @@ Course run synchronization API | Richie - - + +
-
Version: 2.10.0

Course run synchronization API

API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

Synchronization endpoint [/api/1.0/course-runs-sync]

This documentation describes version "1.0" of this API endpoint.

Synchronize a course run [POST]

It takes a JSON object containing the course run details:

  • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - +
    Version: 2.10.0

    Course run synchronization API

    API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

    Synchronization endpoint [/api/1.0/course-runs-sync]

    This documentation describes version "1.0" of this API endpoint.

    Synchronize a course run [POST]

    It takes a JSON object containing the course run details:

    • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - url of the course syllabus on the LMS from which a unique course identifier can be extracted
    • start: 2018-02-01T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course starts
    • end: 2018-02-28T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course ends
    • enrollment_start: 2018-01-01T06:00:00Z (string, optional) - ISO 8601 date, when enrollment @@ -19,7 +19,7 @@ containing the digest of the utf-8 encoded json representation of the submitted data for the given secret key and SHA256 digest algorithm (see [synchronizing-course-runs] for an example).
  • Body

      {
    "resource_link": "https://lms.example.com/courses/course-v1:001+001+001/info",
    "start": "2021-02-01T00:00:00Z",
    "end": "2021-02-31T23:59:59Z",
    "enrollment_start": "2021-01-01T00:00:00Z",
    "enrollment_end": "2021-01-31T23:59:59Z",
    "languages": ["en", "fr"]
    }
  • Response 200 (application/json)

    • Body

        {
      "success": True
      }
  • - - + + \ No newline at end of file diff --git a/docs/2.10.0/building-the-frontend/index.html b/docs/2.10.0/building-the-frontend/index.html index 203df34351..cb7686159f 100644 --- a/docs/2.10.0/building-the-frontend/index.html +++ b/docs/2.10.0/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.10.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.10.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.10.0/contributing-guide/index.html b/docs/2.10.0/contributing-guide/index.html index c385f883ae..c8c85ff66f 100644 --- a/docs/2.10.0/contributing-guide/index.html +++ b/docs/2.10.0/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.10.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.10.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.10.0/css-guidelines/index.html b/docs/2.10.0/css-guidelines/index.html index 8583435d82..cb0bc5caf7 100644 --- a/docs/2.10.0/css-guidelines/index.html +++ b/docs/2.10.0/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.10.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.10.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.10.0/discover/index.html b/docs/2.10.0/discover/index.html index 6b77748497..67ec4eed4c 100644 --- a/docs/2.10.0/discover/index.html +++ b/docs/2.10.0/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
    -
    Version: 2.10.0

    Getting started with Richie

    If you're looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed +

    Version: 2.10.0

    Getting started with Richie

    If you're looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of 4 services:

    • db: the Postgresql database,
    • elasticsearch: the search engine,
    • app: the actual DjangoCMS project with all our application code,
    • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

    At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -24,7 +24,7 @@ a course on the LMS, and the LMS points back to the catalogue to browse courses.

    This approach is used for example on https://www.fun-campus.fr or https://catalogue.edulib.org.

    For a seamless user experience, it is possible to connect a Richie instance to an OpenEdX instance (or some other LMS like Moodle at the cost of minor adaptations), in several ways that we explain in the LMS connection guide.

    - - + + \ No newline at end of file diff --git a/docs/2.10.0/displaying-connection-status/index.html b/docs/2.10.0/displaying-connection-status/index.html index 95d5d810cf..955e518c5b 100644 --- a/docs/2.10.0/displaying-connection-status/index.html +++ b/docs/2.10.0/displaying-connection-status/index.html @@ -4,12 +4,12 @@ Displaying OpenEdX connection status in Richie | Richie - - + +
    -
    Version: 2.10.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status +

    Version: 2.10.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status and display profile information for the logged-in user in Richie.

    In Richie, the only users that are actually authenticated on the DjangoCMS instance producing the site, are editors and staff users. Your instructors and learners will not have user accounts on Richie, but authentication is delegated to a remote service that can be OpenEdX, KeyCloack, or @@ -26,7 +26,7 @@ set for the above authentication delegation BASE_URL setting.

    If you need to bind user data into a url, wrap the property between brackets. For example, the link configured above for the profile page {base_url:s}/u/(username) would point to https://lms.example.com/u/johndoe for a user carrying the username johndoe.

    - - + + \ No newline at end of file diff --git a/docs/2.10.0/django-react-interop/index.html b/docs/2.10.0/django-react-interop/index.html index ee2d129746..a713811bc4 100644 --- a/docs/2.10.0/django-react-interop/index.html +++ b/docs/2.10.0/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.10.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.10.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.10.0/docker-development/index.html b/docs/2.10.0/docker-development/index.html index c6616e7a41..122039ec5a 100644 --- a/docs/2.10.0/docker-development/index.html +++ b/docs/2.10.0/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.10.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.10.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.10.0/frontend-overrides/index.html b/docs/2.10.0/frontend-overrides/index.html index 20568602cb..642917a5b6 100644 --- a/docs/2.10.0/frontend-overrides/index.html +++ b/docs/2.10.0/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.10.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.10.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.10.0/internationalization/index.html b/docs/2.10.0/internationalization/index.html index e467e0bdf4..21ce2f5fcf 100644 --- a/docs/2.10.0/internationalization/index.html +++ b/docs/2.10.0/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.10.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.10.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.10.0/lms-backends/index.html b/docs/2.10.0/lms-backends/index.html index f0f2ddc60f..b724979d24 100644 --- a/docs/2.10.0/lms-backends/index.html +++ b/docs/2.10.0/lms-backends/index.html @@ -4,12 +4,12 @@ Configuring LMS Backends | Richie - - + +
    -
    Version: 2.10.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless +

    Version: 2.10.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless experience between browsing the course catalog on Richie, enrolling to a course and following the course itself on the LMS.

    It is possible to do the same with Moodle or any other LMS exposing an enrollment API, at the cost of writing a custom LMS handler backend.

    Prerequisites

    This connection requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that @@ -36,7 +36,7 @@ value other then manual.

    • Type: enum(string)
    • Required: No
    • Value: for example ["languages"]

    Technical support

    If you encounter an issue with this documentation or the backends included in Richie, please open an issue on our repository.

    If you need a custom backend, you can submit a PR or open an issue and we will consider adding it.

    - - + + \ No newline at end of file diff --git a/docs/2.10.0/lms-connection/index.html b/docs/2.10.0/lms-connection/index.html index 7fe015bf97..e25c7e4a4b 100644 --- a/docs/2.10.0/lms-connection/index.html +++ b/docs/2.10.0/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with one or more LMS | Richie - - + +
    -
    Version: 2.10.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated +

    Version: 2.10.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated seamless experience.

    As of today, each approach has been implemented for OpenEdX but the same could be done for other LMSes like Moodle, at the cost of minor adaptations.

    1. Displaying connection status

    OpenEdX can be configured to allow CORS requests. Doing so allows Richie to retrieve a user's connection status from OpenEdx and display the user's profile information directly on the Richie @@ -31,7 +31,7 @@ docker-compose.

    If you want to activate seamless enrollment locally for development, you will need to set up TLS domains for both Richie and OpenEdX. To do this, head over to our guide on setting-up TLS connections for Richie and OpenEdX.

    - - + + \ No newline at end of file diff --git a/docs/2.10.0/native-installation/index.html b/docs/2.10.0/native-installation/index.html index 0d126e2bcd..4a8902fcdd 100644 --- a/docs/2.10.0/native-installation/index.html +++ b/docs/2.10.0/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.10.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.10.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.10.0/synchronizing-course-runs/index.html b/docs/2.10.0/synchronizing-course-runs/index.html index a613ba18e3..82d81047b8 100644 --- a/docs/2.10.0/synchronizing-course-runs/index.html +++ b/docs/2.10.0/synchronizing-course-runs/index.html @@ -4,12 +4,12 @@ Synchronizing course runs between Richie and OpenEdX | Richie - - + +
    -
    Version: 2.10.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the +

    Version: 2.10.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the RICHIE_COURSE_RUN_SYNC_SECRETS setting with one or more secrets:

    RICHIE_COURSE_RUN_SYNC_SECRETS = ["SharedSecret", "OtherSharedSecret"]

    This setting collects several secrets in order to allow rotating them without any downtime. Any of the secrets listed in this setting can be used to sign your queries.

    Your secret should be shared with the LMS or distant system that needs to synchronize its course runs with the Richie instance. Richie will try the declared secrets one by one until it finds @@ -29,7 +29,7 @@ course run is modified and published.

    Or you can use the Richie Open edX Synchronization which is based on the following code sample and also includes the enrollment count.

    Given a COURSE_HOOK setting defined as follows in your OpenEdX instance:

    COURSE_HOOK = {
    "secret": "SharedSecret",
    "url": "https://richie.example.com/api/v1.0/course-runs-sync/",
    }

    The code for the synchronization function in OpenEdX could look like this:

    import hashlib
    import hmac
    import json

    from django.conf import settings

    from microsite_configuration import microsite
    import requests
    from xmodule.modulestore.django import modulestore


    def update_course(course_key, *args, **kwargs):
    """Synchronize an OpenEdX course, identified by its course key, with a Richie instance."""
    course = modulestore().get_course(course_key)
    edxapp_domain = microsite.get_value("site_domain", settings.LMS_BASE)

    data = {
    "resource_link": "https://{:s}/courses/{!s}/info".format(
    edxapp_domain, course_key
    ),
    "start": course.start and course.start.isoformat(),
    "end": course.end and course.end.isoformat(),
    "enrollment_start": course.enrollment_start and course.enrollment_start.isoformat(),
    "enrollment_end": course.enrollment_end and course.enrollment_end.isoformat(),
    "languages": [course.language or settings.LANGUAGE_CODE],
    }

    signature = hmac.new(
    setting.COURSE_HOOK["secret"].encode("utf-8"),
    msg=json.dumps(data).encode("utf-8"),
    digestmod=hashlib.sha256,
    ).hexdigest()

    response = requests.post(
    setting.COURSE_HOOK["url"],
    json=data,
    headers={"Authorization": "SIG-HMAC-SHA256 {:s}".format(signature)},
    )

    Thanks to the signal emitted in OpenEdX, this function can then be triggered each time a course is modified and published:

    from django.dispatch import receiver
    from xmodule.modulestore.django import SignalHandler


    @receiver(SignalHandler.course_published, dispatch_uid='update_course_on_publish')
    def update_course_on_publish(sender, course_key, **kwargs):
    update_course(course_key)
    - - + + \ No newline at end of file diff --git a/docs/2.10.0/tls-connection/index.html b/docs/2.10.0/tls-connection/index.html index 288acd4f66..3c8b33d3d7 100644 --- a/docs/2.10.0/tls-connection/index.html +++ b/docs/2.10.0/tls-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie and OpenEdX over TLS for development | Richie - - + +
    -
    Version: 2.10.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 +

    Version: 2.10.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 and uses a fake LMS backend (base.BaseLMSBackend) as you can see if you check the RICHIE_LMS_BACKENDS setting in env.d/development.

    This base backend uses session storage to fake enrollments to course runs.

    If you want to test real enrollments to an OpenEdX instance hosted on an external domain, OpenEdX will need to generate a CORS CSRF Cookie. This cookie is flagged as secure, which implies that @@ -32,7 +32,7 @@ to share cookies with the OpenEdx application to allow CORS CSRF Protected XHR requests.

    4. Start Richie and OpenEdx over SSL

    Now, the OpenEdx application should respond on https://edx.local.dev:8073, and Richie on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. The next time you want to use SSL, you can run the following command on both the Richie and OpenEdX projects:

    $ make run-ssl

    Of course, you can still run apps without ssl by using:

    $ make run
    - - + + \ No newline at end of file diff --git a/docs/2.10.0/web-analytics/index.html b/docs/2.10.0/web-analytics/index.html index a370f146b2..06a55c8d13 100644 --- a/docs/2.10.0/web-analytics/index.html +++ b/docs/2.10.0/web-analytics/index.html @@ -4,16 +4,16 @@ Add web analytics to your site | Richie - - + +
    -
    Version: 2.10.0

    Add web analytics to your site

    The purpose of this file is to document how you can enable a Web Analytics solution. +

    Version: 2.10.0

    Add web analytics to your site

    The purpose of this file is to document how you can enable a Web Analytics solution. Currently Richie only supports by default the Google Analytics using the Google Tag Manager Javascript. But it is possible with little cost to add support for other web analytics solutions.

    Google Analytics

    Next, it is decribed how you can configure the Google Analytics on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Analytics tracking id code.

    The current Google Analytics implementation also includes custom dimensions. Those dimensions permit you to create further analyses on Google Analytics or even use them to create custom reports. Custom dimensions with a value as example:

    • Organizations codes - UNIV_LISBON | UNIV_PORTO
    • Course code - COURSE_XPTO
    • Course runs titles - Summer edition | Winter edition
    • Course runs resource links - http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/info
    • Page title - Introduction to Programming

    Location of the web analytics javascript

    Use the WEB_ANALYTICS_LOCATION settings to decide where do you want to put the Javascript code. Use head (default value), to put the Javascript on HTML header, or footer, to put the Javascript code to the bottom of the body.

    Add a new Web Analytics solution

    In this section it's described how you can add support to a different Web Analytics solution.

    • override the richie/web_analytics.html template
    • define the WEB_ANALYTICS_ID setting with your tracking identification
    • define the WEB_ANALYTICS_PROVIDER setting with a value that represents your solution, eg. my-custom-web-analytics-software
    • optionally change WEB_ANALYTICS_LOCATION setting with head (default) or footer value
    • Example of a richie/web_analytics.html file customization that prints to the browser console log the dimension keys and values:
    <script type="text/javascript">
    {% for dimension_key, dimension_value_list in WEB_ANALYTICS.DIMENSIONS.items %}
    console.log("dimension: index '{{forloop.counter}}' with key '{{ dimension_key }}' with value '{{ dimension_value_list|join:" | " }}'");
    {% endfor %}
    </script>

    Output:

    dimension: index '1' with key 'organizations_codes' with value 'COMPATIBLE-EVEN-KEELED-UTILIZATION-19 | FOCUSED-NEXT-GENERATION-FUNCTIONALITIES-22 | UNIVERSAL-MODULAR-LOCAL-AREA-NETWORK-23'
    dimension: index '2' with key 'course_code' with value '00017'
    dimension: index '3' with key 'course_runs_titles' with value 'Run 0'
    dimension: index '4' with key 'course_runs_resource_links' with value ''
    dimension: index '5' with key 'page_title' with value 'Business-focused zero-defect application'

    But you can also contribute to Richie by creating a pull request to add support for a different web analytics solution. In this last case, you have to edit directly the richie/web_analytics.html template.

    Example of an override of the richie/web_analytics.html file:

    {% block web_analytics %}
    {% if WEB_ANALYTICS_ID %}
    {% if WEB_ANALYTICS_PROVIDER == "my_custom_web_analytics_software" %}
    <script type="text/javascript" src="{% static 'myapp/js/custom_web_analytics_software.js' %}">
    <script type="text/javascript">
    // javascript code that startups the custom web analytics software
    </script>
    {% endif %}
    {% endif %}
    {% endblock web_analytics %}

    The web analytics dimensions are being added to the django context using the WEB_ANALYTICS.DIMENSIONS dictionary. Because each dimension value could have multiple values, then each dictionary value is a list. Web analytics dimensions dictionary keys:

    • organizations_codes
    • course_code
    • course_runs_titles
    • course_runs_resource_links
    • page_title

    Example, if you only need the organization codes on your custom richie/web_analytics.html file:

    <script type="text/javascript">
    console.log("organization codes: '{{ WEB_ANALYTICS.DIMENSIONS.organizations_codes |join:" | " }}");
    </script>
    - - + + \ No newline at end of file diff --git a/docs/2.11.0/accessibility-testing/index.html b/docs/2.11.0/accessibility-testing/index.html index c3ccb1d387..7a82facde2 100644 --- a/docs/2.11.0/accessibility-testing/index.html +++ b/docs/2.11.0/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.11.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.11.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.11.0/api/course-run-synchronization-api/index.html b/docs/2.11.0/api/course-run-synchronization-api/index.html index 47af6e8c75..f525ad7055 100644 --- a/docs/2.11.0/api/course-run-synchronization-api/index.html +++ b/docs/2.11.0/api/course-run-synchronization-api/index.html @@ -4,12 +4,12 @@ Course run synchronization API | Richie - - + +
    -
    Version: 2.11.0

    Course run synchronization API

    API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

    Synchronization endpoint [/api/1.0/course-runs-sync]

    This documentation describes version "1.0" of this API endpoint.

    Synchronize a course run [POST]

    It takes a JSON object containing the course run details:

    • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - +
      Version: 2.11.0

      Course run synchronization API

      API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

      Synchronization endpoint [/api/1.0/course-runs-sync]

      This documentation describes version "1.0" of this API endpoint.

      Synchronize a course run [POST]

      It takes a JSON object containing the course run details:

      • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - url of the course syllabus on the LMS from which a unique course identifier can be extracted
      • start: 2018-02-01T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course starts
      • end: 2018-02-28T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course ends
      • enrollment_start: 2018-01-01T06:00:00Z (string, optional) - ISO 8601 date, when enrollment @@ -19,7 +19,7 @@ containing the digest of the utf-8 encoded json representation of the submitted data for the given secret key and SHA256 digest algorithm (see [synchronizing-course-runs] for an example).
    • Body

        {
      "resource_link": "https://lms.example.com/courses/course-v1:001+001+001/info",
      "start": "2021-02-01T00:00:00Z",
      "end": "2021-02-31T23:59:59Z",
      "enrollment_start": "2021-01-01T00:00:00Z",
      "enrollment_end": "2021-01-31T23:59:59Z",
      "languages": ["en", "fr"]
      }
  • Response 200 (application/json)

    • Body

        {
      "success": True
      }
  • - - + + \ No newline at end of file diff --git a/docs/2.11.0/building-the-frontend/index.html b/docs/2.11.0/building-the-frontend/index.html index a3a07e23b3..c24d88c78b 100644 --- a/docs/2.11.0/building-the-frontend/index.html +++ b/docs/2.11.0/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.11.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.11.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.11.0/contributing-guide/index.html b/docs/2.11.0/contributing-guide/index.html index c9cbff60b2..1ef814a4a0 100644 --- a/docs/2.11.0/contributing-guide/index.html +++ b/docs/2.11.0/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.11.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.11.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.11.0/css-guidelines/index.html b/docs/2.11.0/css-guidelines/index.html index d6cb916741..201029a98f 100644 --- a/docs/2.11.0/css-guidelines/index.html +++ b/docs/2.11.0/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.11.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.11.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.11.0/discover/index.html b/docs/2.11.0/discover/index.html index 7608fdee53..3052b0b9a7 100644 --- a/docs/2.11.0/discover/index.html +++ b/docs/2.11.0/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
    -
    Version: 2.11.0

    Getting started with Richie

    If you're looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed +

    Version: 2.11.0

    Getting started with Richie

    If you're looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of 4 services:

    • db: the Postgresql database,
    • elasticsearch: the search engine,
    • app: the actual DjangoCMS project with all our application code,
    • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

    At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -24,7 +24,7 @@ a course on the LMS, and the LMS points back to the catalogue to browse courses.

    This approach is used for example on https://www.fun-campus.fr or https://catalogue.edulib.org.

    For a seamless user experience, it is possible to connect a Richie instance to an OpenEdX instance (or some other LMS like Moodle at the cost of minor adaptations), in several ways that we explain in the LMS connection guide.

    - - + + \ No newline at end of file diff --git a/docs/2.11.0/displaying-connection-status/index.html b/docs/2.11.0/displaying-connection-status/index.html index 853ad36118..55c870d0a6 100644 --- a/docs/2.11.0/displaying-connection-status/index.html +++ b/docs/2.11.0/displaying-connection-status/index.html @@ -4,12 +4,12 @@ Displaying OpenEdX connection status in Richie | Richie - - + +
    -
    Version: 2.11.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status +

    Version: 2.11.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status and display profile information for the logged-in user in Richie.

    In Richie, the only users that are actually authenticated on the DjangoCMS instance producing the site, are editors and staff users. Your instructors and learners will not have user accounts on Richie, but authentication is delegated to a remote service that can be OpenEdX, KeyCloack, or @@ -26,7 +26,7 @@ set for the above authentication delegation BASE_URL setting.

    If you need to bind user data into a url, wrap the property between brackets. For example, the link configured above for the profile page {base_url:s}/u/(username) would point to https://lms.example.com/u/johndoe for a user carrying the username johndoe.

    - - + + \ No newline at end of file diff --git a/docs/2.11.0/django-react-interop/index.html b/docs/2.11.0/django-react-interop/index.html index a8f15c7982..4b75a159c3 100644 --- a/docs/2.11.0/django-react-interop/index.html +++ b/docs/2.11.0/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.11.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.11.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.11.0/docker-development/index.html b/docs/2.11.0/docker-development/index.html index 1bffa5e3b0..ca18d0a64d 100644 --- a/docs/2.11.0/docker-development/index.html +++ b/docs/2.11.0/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.11.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.11.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.11.0/frontend-overrides/index.html b/docs/2.11.0/frontend-overrides/index.html index 64b1245eda..7516780d2e 100644 --- a/docs/2.11.0/frontend-overrides/index.html +++ b/docs/2.11.0/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.11.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.11.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.11.0/internationalization/index.html b/docs/2.11.0/internationalization/index.html index bab5ed327f..5fb6791f57 100644 --- a/docs/2.11.0/internationalization/index.html +++ b/docs/2.11.0/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.11.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.11.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.11.0/lms-backends/index.html b/docs/2.11.0/lms-backends/index.html index db4e88eda1..42071c83c3 100644 --- a/docs/2.11.0/lms-backends/index.html +++ b/docs/2.11.0/lms-backends/index.html @@ -4,12 +4,12 @@ Configuring LMS Backends | Richie - - + +
    -
    Version: 2.11.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless +

    Version: 2.11.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless experience between browsing the course catalog on Richie, enrolling to a course and following the course itself on the LMS.

    It is possible to do the same with Moodle or any other LMS exposing an enrollment API, at the cost of writing a custom LMS handler backend.

    Prerequisites

    This connection requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that @@ -36,7 +36,7 @@ value other then manual.

    • Type: enum(string)
    • Required: No
    • Value: for example ["languages"]

    Technical support

    If you encounter an issue with this documentation or the backends included in Richie, please open an issue on our repository.

    If you need a custom backend, you can submit a PR or open an issue and we will consider adding it.

    - - + + \ No newline at end of file diff --git a/docs/2.11.0/lms-connection/index.html b/docs/2.11.0/lms-connection/index.html index 79704c6880..d8d5cd5b43 100644 --- a/docs/2.11.0/lms-connection/index.html +++ b/docs/2.11.0/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with one or more LMS | Richie - - + +
    -
    Version: 2.11.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated +

    Version: 2.11.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated seamless experience.

    As of today, each approach has been implemented for OpenEdX but the same could be done for other LMSes like Moodle, at the cost of minor adaptations.

    1. Displaying connection status

    OpenEdX can be configured to allow CORS requests. Doing so allows Richie to retrieve a user's connection status from OpenEdx and display the user's profile information directly on the Richie @@ -31,7 +31,7 @@ docker-compose.

    If you want to activate seamless enrollment locally for development, you will need to set up TLS domains for both Richie and OpenEdX. To do this, head over to our guide on setting-up TLS connections for Richie and OpenEdX.

    - - + + \ No newline at end of file diff --git a/docs/2.11.0/native-installation/index.html b/docs/2.11.0/native-installation/index.html index 338a84a12a..b33fcb79da 100644 --- a/docs/2.11.0/native-installation/index.html +++ b/docs/2.11.0/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.11.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.11.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.11.0/synchronizing-course-runs/index.html b/docs/2.11.0/synchronizing-course-runs/index.html index 1c46024a4a..4d95078e60 100644 --- a/docs/2.11.0/synchronizing-course-runs/index.html +++ b/docs/2.11.0/synchronizing-course-runs/index.html @@ -4,12 +4,12 @@ Synchronizing course runs between Richie and OpenEdX | Richie - - + +
    -
    Version: 2.11.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the +

    Version: 2.11.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the RICHIE_COURSE_RUN_SYNC_SECRETS setting with one or more secrets:

    RICHIE_COURSE_RUN_SYNC_SECRETS = ["SharedSecret", "OtherSharedSecret"]

    This setting collects several secrets in order to allow rotating them without any downtime. Any of the secrets listed in this setting can be used to sign your queries.

    Your secret should be shared with the LMS or distant system that needs to synchronize its course runs with the Richie instance. Richie will try the declared secrets one by one until it finds @@ -29,7 +29,7 @@ course run is modified and published.

    Or you can use the Richie Open edX Synchronization which is based on the following code sample and also includes the enrollment count.

    Given a COURSE_HOOK setting defined as follows in your OpenEdX instance:

    COURSE_HOOK = {
    "secret": "SharedSecret",
    "url": "https://richie.example.com/api/v1.0/course-runs-sync/",
    }

    The code for the synchronization function in OpenEdX could look like this:

    import hashlib
    import hmac
    import json

    from django.conf import settings

    from microsite_configuration import microsite
    import requests
    from xmodule.modulestore.django import modulestore


    def update_course(course_key, *args, **kwargs):
    """Synchronize an OpenEdX course, identified by its course key, with a Richie instance."""
    course = modulestore().get_course(course_key)
    edxapp_domain = microsite.get_value("site_domain", settings.LMS_BASE)

    data = {
    "resource_link": "https://{:s}/courses/{!s}/info".format(
    edxapp_domain, course_key
    ),
    "start": course.start and course.start.isoformat(),
    "end": course.end and course.end.isoformat(),
    "enrollment_start": course.enrollment_start and course.enrollment_start.isoformat(),
    "enrollment_end": course.enrollment_end and course.enrollment_end.isoformat(),
    "languages": [course.language or settings.LANGUAGE_CODE],
    }

    signature = hmac.new(
    setting.COURSE_HOOK["secret"].encode("utf-8"),
    msg=json.dumps(data).encode("utf-8"),
    digestmod=hashlib.sha256,
    ).hexdigest()

    response = requests.post(
    setting.COURSE_HOOK["url"],
    json=data,
    headers={"Authorization": "SIG-HMAC-SHA256 {:s}".format(signature)},
    )

    Thanks to the signal emitted in OpenEdX, this function can then be triggered each time a course is modified and published:

    from django.dispatch import receiver
    from xmodule.modulestore.django import SignalHandler


    @receiver(SignalHandler.course_published, dispatch_uid='update_course_on_publish')
    def update_course_on_publish(sender, course_key, **kwargs):
    update_course(course_key)
    - - + + \ No newline at end of file diff --git a/docs/2.11.0/tls-connection/index.html b/docs/2.11.0/tls-connection/index.html index b540af0580..8651ecb9c9 100644 --- a/docs/2.11.0/tls-connection/index.html +++ b/docs/2.11.0/tls-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie and OpenEdX over TLS for development | Richie - - + +
    -
    Version: 2.11.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 +

    Version: 2.11.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 and uses a fake LMS backend (base.BaseLMSBackend) as you can see if you check the RICHIE_LMS_BACKENDS setting in env.d/development.

    This base backend uses session storage to fake enrollments to course runs.

    If you want to test real enrollments to an OpenEdX instance hosted on an external domain, OpenEdX will need to generate a CORS CSRF Cookie. This cookie is flagged as secure, which implies that @@ -32,7 +32,7 @@ to share cookies with the OpenEdx application to allow CORS CSRF Protected XHR requests.

    4. Start Richie and OpenEdx over SSL

    Now, the OpenEdx application should respond on https://edx.local.dev:8073, and Richie on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. The next time you want to use SSL, you can run the following command on both the Richie and OpenEdX projects:

    $ make run-ssl

    Of course, you can still run apps without ssl by using:

    $ make run
    - - + + \ No newline at end of file diff --git a/docs/2.11.0/web-analytics/index.html b/docs/2.11.0/web-analytics/index.html index cf0f025882..07fb31c951 100644 --- a/docs/2.11.0/web-analytics/index.html +++ b/docs/2.11.0/web-analytics/index.html @@ -4,16 +4,16 @@ Add web analytics to your site | Richie - - + +
    -
    Version: 2.11.0

    Add web analytics to your site

    The purpose of this file is to document how you can enable a Web Analytics solution. +

    Version: 2.11.0

    Add web analytics to your site

    The purpose of this file is to document how you can enable a Web Analytics solution. Currently Richie only supports by default the Google Analytics using the Google Tag Manager Javascript. But it is possible with little cost to add support for other web analytics solutions.

    Google Analytics

    Next, it is decribed how you can configure the Google Analytics on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Analytics tracking id code.

    The current Google Analytics implementation also includes custom dimensions. Those dimensions permit you to create further analyses on Google Analytics or even use them to create custom reports. Custom dimensions with a value as example:

    • Organizations codes - UNIV_LISBON | UNIV_PORTO
    • Course code - COURSE_XPTO
    • Course runs titles - Summer edition | Winter edition
    • Course runs resource links - http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/info
    • Page title - Introduction to Programming

    Location of the web analytics javascript

    Use the WEB_ANALYTICS_LOCATION settings to decide where do you want to put the Javascript code. Use head (default value), to put the Javascript on HTML header, or footer, to put the Javascript code to the bottom of the body.

    Add a new Web Analytics solution

    In this section it's described how you can add support to a different Web Analytics solution.

    • override the richie/web_analytics.html template
    • define the WEB_ANALYTICS_ID setting with your tracking identification
    • define the WEB_ANALYTICS_PROVIDER setting with a value that represents your solution, eg. my-custom-web-analytics-software
    • optionally change WEB_ANALYTICS_LOCATION setting with head (default) or footer value
    • Example of a richie/web_analytics.html file customization that prints to the browser console log the dimension keys and values:
    <script type="text/javascript">
    {% for dimension_key, dimension_value_list in WEB_ANALYTICS.DIMENSIONS.items %}
    console.log("dimension: index '{{forloop.counter}}' with key '{{ dimension_key }}' with value '{{ dimension_value_list|join:" | " }}'");
    {% endfor %}
    </script>

    Output:

    dimension: index '1' with key 'organizations_codes' with value 'COMPATIBLE-EVEN-KEELED-UTILIZATION-19 | FOCUSED-NEXT-GENERATION-FUNCTIONALITIES-22 | UNIVERSAL-MODULAR-LOCAL-AREA-NETWORK-23'
    dimension: index '2' with key 'course_code' with value '00017'
    dimension: index '3' with key 'course_runs_titles' with value 'Run 0'
    dimension: index '4' with key 'course_runs_resource_links' with value ''
    dimension: index '5' with key 'page_title' with value 'Business-focused zero-defect application'

    But you can also contribute to Richie by creating a pull request to add support for a different web analytics solution. In this last case, you have to edit directly the richie/web_analytics.html template.

    Example of an override of the richie/web_analytics.html file:

    {% block web_analytics %}
    {% if WEB_ANALYTICS_ID %}
    {% if WEB_ANALYTICS_PROVIDER == "my_custom_web_analytics_software" %}
    <script type="text/javascript" src="{% static 'myapp/js/custom_web_analytics_software.js' %}">
    <script type="text/javascript">
    // javascript code that startups the custom web analytics software
    </script>
    {% endif %}
    {% endif %}
    {% endblock web_analytics %}

    The web analytics dimensions are being added to the django context using the WEB_ANALYTICS.DIMENSIONS dictionary. Because each dimension value could have multiple values, then each dictionary value is a list. Web analytics dimensions dictionary keys:

    • organizations_codes
    • course_code
    • course_runs_titles
    • course_runs_resource_links
    • page_title

    Example, if you only need the organization codes on your custom richie/web_analytics.html file:

    <script type="text/javascript">
    console.log("organization codes: '{{ WEB_ANALYTICS.DIMENSIONS.organizations_codes |join:" | " }}");
    </script>
    - - + + \ No newline at end of file diff --git a/docs/2.12.0/accessibility-testing/index.html b/docs/2.12.0/accessibility-testing/index.html index d2571f1aa9..b482f14854 100644 --- a/docs/2.12.0/accessibility-testing/index.html +++ b/docs/2.12.0/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.12.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.12.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.12.0/api/course-run-synchronization-api/index.html b/docs/2.12.0/api/course-run-synchronization-api/index.html index cb30ff1ad4..17dcb1e153 100644 --- a/docs/2.12.0/api/course-run-synchronization-api/index.html +++ b/docs/2.12.0/api/course-run-synchronization-api/index.html @@ -4,12 +4,12 @@ Course run synchronization API | Richie - - + +
    -
    Version: 2.12.0

    Course run synchronization API

    API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

    Synchronization endpoint [/api/1.0/course-runs-sync]

    This documentation describes version "1.0" of this API endpoint.

    Synchronize a course run [POST]

    It takes a JSON object containing the course run details:

    • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - +
      Version: 2.12.0

      Course run synchronization API

      API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

      Synchronization endpoint [/api/1.0/course-runs-sync]

      This documentation describes version "1.0" of this API endpoint.

      Synchronize a course run [POST]

      It takes a JSON object containing the course run details:

      • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - url of the course syllabus on the LMS from which a unique course identifier can be extracted
      • start: 2018-02-01T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course starts
      • end: 2018-02-28T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course ends
      • enrollment_start: 2018-01-01T06:00:00Z (string, optional) - ISO 8601 date, when enrollment @@ -19,7 +19,7 @@ containing the digest of the utf-8 encoded json representation of the submitted data for the given secret key and SHA256 digest algorithm (see [synchronizing-course-runs] for an example).
    • Body

        {
      "resource_link": "https://lms.example.com/courses/course-v1:001+001+001/info",
      "start": "2021-02-01T00:00:00Z",
      "end": "2021-02-31T23:59:59Z",
      "enrollment_start": "2021-01-01T00:00:00Z",
      "enrollment_end": "2021-01-31T23:59:59Z",
      "languages": ["en", "fr"]
      }
  • Response 200 (application/json)

    • Body

        {
      "success": True
      }
  • - - + + \ No newline at end of file diff --git a/docs/2.12.0/building-the-frontend/index.html b/docs/2.12.0/building-the-frontend/index.html index 9a13ee1855..9972dc92ff 100644 --- a/docs/2.12.0/building-the-frontend/index.html +++ b/docs/2.12.0/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.12.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.12.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.12.0/contributing-guide/index.html b/docs/2.12.0/contributing-guide/index.html index 079d4de297..b27594a271 100644 --- a/docs/2.12.0/contributing-guide/index.html +++ b/docs/2.12.0/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.12.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.12.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.12.0/css-guidelines/index.html b/docs/2.12.0/css-guidelines/index.html index fc317ef96d..d6a0065b88 100644 --- a/docs/2.12.0/css-guidelines/index.html +++ b/docs/2.12.0/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.12.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.12.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.12.0/discover/index.html b/docs/2.12.0/discover/index.html index ef5591b5e6..80db8e67f2 100644 --- a/docs/2.12.0/discover/index.html +++ b/docs/2.12.0/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
    -
    Version: 2.12.0

    Getting started with Richie

    If you're looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed +

    Version: 2.12.0

    Getting started with Richie

    If you're looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of 4 services:

    • db: the Postgresql database,
    • elasticsearch: the search engine,
    • app: the actual DjangoCMS project with all our application code,
    • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

    At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -24,7 +24,7 @@ a course on the LMS, and the LMS points back to the catalogue to browse courses.

    This approach is used for example on https://www.fun-campus.fr or https://catalogue.edulib.org.

    For a seamless user experience, it is possible to connect a Richie instance to an OpenEdX instance (or some other LMS like Moodle at the cost of minor adaptations), in several ways that we explain in the LMS connection guide.

    - - + + \ No newline at end of file diff --git a/docs/2.12.0/displaying-connection-status/index.html b/docs/2.12.0/displaying-connection-status/index.html index 132175bb12..1ef87479a7 100644 --- a/docs/2.12.0/displaying-connection-status/index.html +++ b/docs/2.12.0/displaying-connection-status/index.html @@ -4,12 +4,12 @@ Displaying OpenEdX connection status in Richie | Richie - - + +
    -
    Version: 2.12.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status +

    Version: 2.12.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status and display profile information for the logged-in user in Richie.

    In Richie, the only users that are actually authenticated on the DjangoCMS instance producing the site, are editors and staff users. Your instructors and learners will not have user accounts on Richie, but authentication is delegated to a remote service that can be OpenEdX, KeyCloack, or @@ -26,7 +26,7 @@ set for the above authentication delegation BASE_URL setting.

    If you need to bind user data into a url, wrap the property between brackets. For example, the link configured above for the profile page {base_url:s}/u/(username) would point to https://lms.example.com/u/johndoe for a user carrying the username johndoe.

    - - + + \ No newline at end of file diff --git a/docs/2.12.0/django-react-interop/index.html b/docs/2.12.0/django-react-interop/index.html index fec6b62480..b5719130e5 100644 --- a/docs/2.12.0/django-react-interop/index.html +++ b/docs/2.12.0/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.12.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.12.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.12.0/docker-development/index.html b/docs/2.12.0/docker-development/index.html index 7995e39f3c..789f9ed14f 100644 --- a/docs/2.12.0/docker-development/index.html +++ b/docs/2.12.0/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.12.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.12.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.12.0/frontend-overrides/index.html b/docs/2.12.0/frontend-overrides/index.html index 0ae2a8e1a3..4f87e72d78 100644 --- a/docs/2.12.0/frontend-overrides/index.html +++ b/docs/2.12.0/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.12.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.12.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.12.0/internationalization/index.html b/docs/2.12.0/internationalization/index.html index 0de05cd679..a85aed57b4 100644 --- a/docs/2.12.0/internationalization/index.html +++ b/docs/2.12.0/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.12.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.12.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.12.0/lms-backends/index.html b/docs/2.12.0/lms-backends/index.html index 48b43b68e1..1a4a49c6ca 100644 --- a/docs/2.12.0/lms-backends/index.html +++ b/docs/2.12.0/lms-backends/index.html @@ -4,12 +4,12 @@ Configuring LMS Backends | Richie - - + +
    -
    Version: 2.12.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless +

    Version: 2.12.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless experience between browsing the course catalog on Richie, enrolling to a course and following the course itself on the LMS.

    It is possible to do the same with Moodle or any other LMS exposing an enrollment API, at the cost of writing a custom LMS handler backend.

    Prerequisites

    This connection requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that @@ -36,7 +36,7 @@ value other then manual.

    • Type: enum(string)
    • Required: No
    • Value: for example ["languages"]

    Technical support

    If you encounter an issue with this documentation or the backends included in Richie, please open an issue on our repository.

    If you need a custom backend, you can submit a PR or open an issue and we will consider adding it.

    - - + + \ No newline at end of file diff --git a/docs/2.12.0/lms-connection/index.html b/docs/2.12.0/lms-connection/index.html index 45bf8bb687..f7ca7fb0c7 100644 --- a/docs/2.12.0/lms-connection/index.html +++ b/docs/2.12.0/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with one or more LMS | Richie - - + +
    -
    Version: 2.12.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated +

    Version: 2.12.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated seamless experience.

    As of today, each approach has been implemented for OpenEdX but the same could be done for other LMSes like Moodle, at the cost of minor adaptations.

    1. Displaying connection status

    OpenEdX can be configured to allow CORS requests. Doing so allows Richie to retrieve a user's connection status from OpenEdx and display the user's profile information directly on the Richie @@ -31,7 +31,7 @@ docker-compose.

    If you want to activate seamless enrollment locally for development, you will need to set up TLS domains for both Richie and OpenEdX. To do this, head over to our guide on setting-up TLS connections for Richie and OpenEdX.

    - - + + \ No newline at end of file diff --git a/docs/2.12.0/native-installation/index.html b/docs/2.12.0/native-installation/index.html index 6405538e17..ff90c636ba 100644 --- a/docs/2.12.0/native-installation/index.html +++ b/docs/2.12.0/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.12.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.12.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.12.0/synchronizing-course-runs/index.html b/docs/2.12.0/synchronizing-course-runs/index.html index 77c75f935f..54d88c5b24 100644 --- a/docs/2.12.0/synchronizing-course-runs/index.html +++ b/docs/2.12.0/synchronizing-course-runs/index.html @@ -4,12 +4,12 @@ Synchronizing course runs between Richie and OpenEdX | Richie - - + +
    -
    Version: 2.12.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the +

    Version: 2.12.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the RICHIE_COURSE_RUN_SYNC_SECRETS setting with one or more secrets:

    RICHIE_COURSE_RUN_SYNC_SECRETS = ["SharedSecret", "OtherSharedSecret"]

    This setting collects several secrets in order to allow rotating them without any downtime. Any of the secrets listed in this setting can be used to sign your queries.

    Your secret should be shared with the LMS or distant system that needs to synchronize its course runs with the Richie instance. Richie will try the declared secrets one by one until it finds @@ -29,7 +29,7 @@ course run is modified and published.

    Or you can use the Richie Open edX Synchronization which is based on the following code sample and also includes the enrollment count.

    Given a COURSE_HOOK setting defined as follows in your OpenEdX instance:

    COURSE_HOOK = {
    "secret": "SharedSecret",
    "url": "https://richie.example.com/api/v1.0/course-runs-sync/",
    }

    The code for the synchronization function in OpenEdX could look like this:

    import hashlib
    import hmac
    import json

    from django.conf import settings

    from microsite_configuration import microsite
    import requests
    from xmodule.modulestore.django import modulestore


    def update_course(course_key, *args, **kwargs):
    """Synchronize an OpenEdX course, identified by its course key, with a Richie instance."""
    course = modulestore().get_course(course_key)
    edxapp_domain = microsite.get_value("site_domain", settings.LMS_BASE)

    data = {
    "resource_link": "https://{:s}/courses/{!s}/info".format(
    edxapp_domain, course_key
    ),
    "start": course.start and course.start.isoformat(),
    "end": course.end and course.end.isoformat(),
    "enrollment_start": course.enrollment_start and course.enrollment_start.isoformat(),
    "enrollment_end": course.enrollment_end and course.enrollment_end.isoformat(),
    "languages": [course.language or settings.LANGUAGE_CODE],
    }

    signature = hmac.new(
    setting.COURSE_HOOK["secret"].encode("utf-8"),
    msg=json.dumps(data).encode("utf-8"),
    digestmod=hashlib.sha256,
    ).hexdigest()

    response = requests.post(
    setting.COURSE_HOOK["url"],
    json=data,
    headers={"Authorization": "SIG-HMAC-SHA256 {:s}".format(signature)},
    )

    Thanks to the signal emitted in OpenEdX, this function can then be triggered each time a course is modified and published:

    from django.dispatch import receiver
    from xmodule.modulestore.django import SignalHandler


    @receiver(SignalHandler.course_published, dispatch_uid='update_course_on_publish')
    def update_course_on_publish(sender, course_key, **kwargs):
    update_course(course_key)
    - - + + \ No newline at end of file diff --git a/docs/2.12.0/tls-connection/index.html b/docs/2.12.0/tls-connection/index.html index eb8250ec07..99c32eb832 100644 --- a/docs/2.12.0/tls-connection/index.html +++ b/docs/2.12.0/tls-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie and OpenEdX over TLS for development | Richie - - + +
    -
    Version: 2.12.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 +

    Version: 2.12.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 and uses a fake LMS backend (base.BaseLMSBackend) as you can see if you check the RICHIE_LMS_BACKENDS setting in env.d/development.

    This base backend uses session storage to fake enrollments to course runs.

    If you want to test real enrollments to an OpenEdX instance hosted on an external domain, OpenEdX will need to generate a CORS CSRF Cookie. This cookie is flagged as secure, which implies that @@ -32,7 +32,7 @@ to share cookies with the OpenEdx application to allow CORS CSRF Protected XHR requests.

    4. Start Richie and OpenEdx over SSL

    Now, the OpenEdx application should respond on https://edx.local.dev:8073, and Richie on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. The next time you want to use SSL, you can run the following command on both the Richie and OpenEdX projects:

    $ make run-ssl

    Of course, you can still run apps without ssl by using:

    $ make run
    - - + + \ No newline at end of file diff --git a/docs/2.12.0/web-analytics/index.html b/docs/2.12.0/web-analytics/index.html index 72a058dcb6..4b0e8d0ad5 100644 --- a/docs/2.12.0/web-analytics/index.html +++ b/docs/2.12.0/web-analytics/index.html @@ -4,16 +4,16 @@ Add web analytics to your site | Richie - - + +
    -
    Version: 2.12.0

    Add web analytics to your site

    The purpose of this file is to document how you can enable a Web Analytics solution. +

    Version: 2.12.0

    Add web analytics to your site

    The purpose of this file is to document how you can enable a Web Analytics solution. Currently Richie only supports by default the Google Analytics using the Google Tag Manager Javascript. But it is possible with little cost to add support for other web analytics solutions.

    Google Analytics

    Next, it is decribed how you can configure the Google Analytics on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Analytics tracking id code.

    The current Google Analytics implementation also includes custom dimensions. Those dimensions permit you to create further analyses on Google Analytics or even use them to create custom reports. Custom dimensions with a value as example:

    • Organizations codes - UNIV_LISBON | UNIV_PORTO
    • Course code - COURSE_XPTO
    • Course runs titles - Summer edition | Winter edition
    • Course runs resource links - http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/info
    • Page title - Introduction to Programming

    Location of the web analytics javascript

    Use the WEB_ANALYTICS_LOCATION settings to decide where do you want to put the Javascript code. Use head (default value), to put the Javascript on HTML header, or footer, to put the Javascript code to the bottom of the body.

    Add a new Web Analytics solution

    In this section it's described how you can add support to a different Web Analytics solution.

    • override the richie/web_analytics.html template
    • define the WEB_ANALYTICS_ID setting with your tracking identification
    • define the WEB_ANALYTICS_PROVIDER setting with a value that represents your solution, eg. my-custom-web-analytics-software
    • optionally change WEB_ANALYTICS_LOCATION setting with head (default) or footer value
    • Example of a richie/web_analytics.html file customization that prints to the browser console log the dimension keys and values:
    <script type="text/javascript">
    {% for dimension_key, dimension_value_list in WEB_ANALYTICS.DIMENSIONS.items %}
    console.log("dimension: index '{{forloop.counter}}' with key '{{ dimension_key }}' with value '{{ dimension_value_list|join:" | " }}'");
    {% endfor %}
    </script>

    Output:

    dimension: index '1' with key 'organizations_codes' with value 'COMPATIBLE-EVEN-KEELED-UTILIZATION-19 | FOCUSED-NEXT-GENERATION-FUNCTIONALITIES-22 | UNIVERSAL-MODULAR-LOCAL-AREA-NETWORK-23'
    dimension: index '2' with key 'course_code' with value '00017'
    dimension: index '3' with key 'course_runs_titles' with value 'Run 0'
    dimension: index '4' with key 'course_runs_resource_links' with value ''
    dimension: index '5' with key 'page_title' with value 'Business-focused zero-defect application'

    But you can also contribute to Richie by creating a pull request to add support for a different web analytics solution. In this last case, you have to edit directly the richie/web_analytics.html template.

    Example of an override of the richie/web_analytics.html file:

    {% block web_analytics %}
    {% if WEB_ANALYTICS_ID %}
    {% if WEB_ANALYTICS_PROVIDER == "my_custom_web_analytics_software" %}
    <script type="text/javascript" src="{% static 'myapp/js/custom_web_analytics_software.js' %}">
    <script type="text/javascript">
    // javascript code that startups the custom web analytics software
    </script>
    {% endif %}
    {% endif %}
    {% endblock web_analytics %}

    The web analytics dimensions are being added to the django context using the WEB_ANALYTICS.DIMENSIONS dictionary. Because each dimension value could have multiple values, then each dictionary value is a list. Web analytics dimensions dictionary keys:

    • organizations_codes
    • course_code
    • course_runs_titles
    • course_runs_resource_links
    • page_title

    Example, if you only need the organization codes on your custom richie/web_analytics.html file:

    <script type="text/javascript">
    console.log("organization codes: '{{ WEB_ANALYTICS.DIMENSIONS.organizations_codes |join:" | " }}");
    </script>
    - - + + \ No newline at end of file diff --git a/docs/2.13.0/accessibility-testing/index.html b/docs/2.13.0/accessibility-testing/index.html index db1ebde9b7..4195a75030 100644 --- a/docs/2.13.0/accessibility-testing/index.html +++ b/docs/2.13.0/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.13.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.13.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.13.0/api/course-run-synchronization-api/index.html b/docs/2.13.0/api/course-run-synchronization-api/index.html index 36b76a5706..5eb54bae4d 100644 --- a/docs/2.13.0/api/course-run-synchronization-api/index.html +++ b/docs/2.13.0/api/course-run-synchronization-api/index.html @@ -4,12 +4,12 @@ Course run synchronization API | Richie - - + +
    -
    Version: 2.13.0

    Course run synchronization API

    API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

    Synchronization endpoint [/api/1.0/course-runs-sync]

    This documentation describes version "1.0" of this API endpoint.

    Synchronize a course run [POST]

    It takes a JSON object containing the course run details:

    • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - +
      Version: 2.13.0

      Course run synchronization API

      API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

      Synchronization endpoint [/api/1.0/course-runs-sync]

      This documentation describes version "1.0" of this API endpoint.

      Synchronize a course run [POST]

      It takes a JSON object containing the course run details:

      • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - url of the course syllabus on the LMS from which a unique course identifier can be extracted
      • start: 2018-02-01T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course starts
      • end: 2018-02-28T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course ends
      • enrollment_start: 2018-01-01T06:00:00Z (string, optional) - ISO 8601 date, when enrollment @@ -19,7 +19,7 @@ containing the digest of the utf-8 encoded json representation of the submitted data for the given secret key and SHA256 digest algorithm (see [synchronizing-course-runs] for an example).
    • Body

        {
      "resource_link": "https://lms.example.com/courses/course-v1:001+001+001/info",
      "start": "2021-02-01T00:00:00Z",
      "end": "2021-02-31T23:59:59Z",
      "enrollment_start": "2021-01-01T00:00:00Z",
      "enrollment_end": "2021-01-31T23:59:59Z",
      "languages": ["en", "fr"]
      }
  • Response 200 (application/json)

    • Body

        {
      "success": True
      }
  • - - + + \ No newline at end of file diff --git a/docs/2.13.0/building-the-frontend/index.html b/docs/2.13.0/building-the-frontend/index.html index 55226f5973..c13654677b 100644 --- a/docs/2.13.0/building-the-frontend/index.html +++ b/docs/2.13.0/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.13.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.13.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.13.0/contributing-guide/index.html b/docs/2.13.0/contributing-guide/index.html index 84ff45fd1f..11094765b9 100644 --- a/docs/2.13.0/contributing-guide/index.html +++ b/docs/2.13.0/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.13.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.13.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.13.0/css-guidelines/index.html b/docs/2.13.0/css-guidelines/index.html index 2ea44dc62c..246cf43e0f 100644 --- a/docs/2.13.0/css-guidelines/index.html +++ b/docs/2.13.0/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.13.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.13.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.13.0/discover/index.html b/docs/2.13.0/discover/index.html index 44d92098de..6384f89170 100644 --- a/docs/2.13.0/discover/index.html +++ b/docs/2.13.0/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
    -
    Version: 2.13.0

    Getting started with Richie

    If you're looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed +

    Version: 2.13.0

    Getting started with Richie

    If you're looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of 4 services:

    • db: the Postgresql database,
    • elasticsearch: the search engine,
    • app: the actual DjangoCMS project with all our application code,
    • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

    At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -24,7 +24,7 @@ a course on the LMS, and the LMS points back to the catalogue to browse courses.

    This approach is used for example on https://www.fun-campus.fr or https://catalogue.edulib.org.

    For a seamless user experience, it is possible to connect a Richie instance to an OpenEdX instance (or some other LMS like Moodle at the cost of minor adaptations), in several ways that we explain in the LMS connection guide.

    - - + + \ No newline at end of file diff --git a/docs/2.13.0/displaying-connection-status/index.html b/docs/2.13.0/displaying-connection-status/index.html index daaab58c85..5720fa008e 100644 --- a/docs/2.13.0/displaying-connection-status/index.html +++ b/docs/2.13.0/displaying-connection-status/index.html @@ -4,12 +4,12 @@ Displaying OpenEdX connection status in Richie | Richie - - + +
    -
    Version: 2.13.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status +

    Version: 2.13.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status and display profile information for the logged-in user in Richie.

    In Richie, the only users that are actually authenticated on the DjangoCMS instance producing the site, are editors and staff users. Your instructors and learners will not have user accounts on Richie, but authentication is delegated to a remote service that can be OpenEdX, KeyCloack, or @@ -26,7 +26,7 @@ set for the above authentication delegation BASE_URL setting.

    If you need to bind user data into a url, wrap the property between brackets. For example, the link configured above for the profile page {base_url:s}/u/(username) would point to https://lms.example.com/u/johndoe for a user carrying the username johndoe.

    - - + + \ No newline at end of file diff --git a/docs/2.13.0/django-react-interop/index.html b/docs/2.13.0/django-react-interop/index.html index f3325dee1b..179b40e023 100644 --- a/docs/2.13.0/django-react-interop/index.html +++ b/docs/2.13.0/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.13.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.13.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.13.0/docker-development/index.html b/docs/2.13.0/docker-development/index.html index 19060f93a0..ee90ff4f7e 100644 --- a/docs/2.13.0/docker-development/index.html +++ b/docs/2.13.0/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.13.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.13.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.13.0/frontend-overrides/index.html b/docs/2.13.0/frontend-overrides/index.html index dcb1eb0fd4..1236f0ab23 100644 --- a/docs/2.13.0/frontend-overrides/index.html +++ b/docs/2.13.0/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.13.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.13.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.13.0/internationalization/index.html b/docs/2.13.0/internationalization/index.html index 1926140a2e..0178a17562 100644 --- a/docs/2.13.0/internationalization/index.html +++ b/docs/2.13.0/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.13.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.13.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.13.0/lms-backends/index.html b/docs/2.13.0/lms-backends/index.html index 289b783adc..8813993c23 100644 --- a/docs/2.13.0/lms-backends/index.html +++ b/docs/2.13.0/lms-backends/index.html @@ -4,12 +4,12 @@ Configuring LMS Backends | Richie - - + +
    -
    Version: 2.13.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless +

    Version: 2.13.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless experience between browsing the course catalog on Richie, enrolling to a course and following the course itself on the LMS.

    It is possible to do the same with Moodle or any other LMS exposing an enrollment API, at the cost of writing a custom LMS handler backend.

    Prerequisites

    This connection requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that @@ -36,7 +36,7 @@ value other then manual.

    • Type: enum(string)
    • Required: No
    • Value: for example ["languages"]

    Technical support

    If you encounter an issue with this documentation or the backends included in Richie, please open an issue on our repository.

    If you need a custom backend, you can submit a PR or open an issue and we will consider adding it.

    - - + + \ No newline at end of file diff --git a/docs/2.13.0/lms-connection/index.html b/docs/2.13.0/lms-connection/index.html index 1969994e03..d146ab6ee8 100644 --- a/docs/2.13.0/lms-connection/index.html +++ b/docs/2.13.0/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with one or more LMS | Richie - - + +
    -
    Version: 2.13.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated +

    Version: 2.13.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated seamless experience.

    As of today, each approach has been implemented for OpenEdX but the same could be done for other LMSes like Moodle, at the cost of minor adaptations.

    1. Displaying connection status

    OpenEdX can be configured to allow CORS requests. Doing so allows Richie to retrieve a user's connection status from OpenEdx and display the user's profile information directly on the Richie @@ -31,7 +31,7 @@ docker-compose.

    If you want to activate seamless enrollment locally for development, you will need to set up TLS domains for both Richie and OpenEdX. To do this, head over to our guide on setting-up TLS connections for Richie and OpenEdX.

    - - + + \ No newline at end of file diff --git a/docs/2.13.0/native-installation/index.html b/docs/2.13.0/native-installation/index.html index 5c06c50b8a..68cf0d1467 100644 --- a/docs/2.13.0/native-installation/index.html +++ b/docs/2.13.0/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.13.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.13.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.13.0/synchronizing-course-runs/index.html b/docs/2.13.0/synchronizing-course-runs/index.html index cdac904f9b..7918a7d08a 100644 --- a/docs/2.13.0/synchronizing-course-runs/index.html +++ b/docs/2.13.0/synchronizing-course-runs/index.html @@ -4,12 +4,12 @@ Synchronizing course runs between Richie and OpenEdX | Richie - - + +
    -
    Version: 2.13.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the +

    Version: 2.13.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the RICHIE_COURSE_RUN_SYNC_SECRETS setting with one or more secrets:

    RICHIE_COURSE_RUN_SYNC_SECRETS = ["SharedSecret", "OtherSharedSecret"]

    This setting collects several secrets in order to allow rotating them without any downtime. Any of the secrets listed in this setting can be used to sign your queries.

    Your secret should be shared with the LMS or distant system that needs to synchronize its course runs with the Richie instance. Richie will try the declared secrets one by one until it finds @@ -29,7 +29,7 @@ course run is modified and published.

    Or you can use the Richie Open edX Synchronization which is based on the following code sample and also includes the enrollment count.

    Given a COURSE_HOOK setting defined as follows in your OpenEdX instance:

    COURSE_HOOK = {
    "secret": "SharedSecret",
    "url": "https://richie.example.com/api/v1.0/course-runs-sync/",
    }

    The code for the synchronization function in OpenEdX could look like this:

    import hashlib
    import hmac
    import json

    from django.conf import settings

    from microsite_configuration import microsite
    import requests
    from xmodule.modulestore.django import modulestore


    def update_course(course_key, *args, **kwargs):
    """Synchronize an OpenEdX course, identified by its course key, with a Richie instance."""
    course = modulestore().get_course(course_key)
    edxapp_domain = microsite.get_value("site_domain", settings.LMS_BASE)

    data = {
    "resource_link": "https://{:s}/courses/{!s}/info".format(
    edxapp_domain, course_key
    ),
    "start": course.start and course.start.isoformat(),
    "end": course.end and course.end.isoformat(),
    "enrollment_start": course.enrollment_start and course.enrollment_start.isoformat(),
    "enrollment_end": course.enrollment_end and course.enrollment_end.isoformat(),
    "languages": [course.language or settings.LANGUAGE_CODE],
    }

    signature = hmac.new(
    setting.COURSE_HOOK["secret"].encode("utf-8"),
    msg=json.dumps(data).encode("utf-8"),
    digestmod=hashlib.sha256,
    ).hexdigest()

    response = requests.post(
    setting.COURSE_HOOK["url"],
    json=data,
    headers={"Authorization": "SIG-HMAC-SHA256 {:s}".format(signature)},
    )

    Thanks to the signal emitted in OpenEdX, this function can then be triggered each time a course is modified and published:

    from django.dispatch import receiver
    from xmodule.modulestore.django import SignalHandler


    @receiver(SignalHandler.course_published, dispatch_uid='update_course_on_publish')
    def update_course_on_publish(sender, course_key, **kwargs):
    update_course(course_key)
    - - + + \ No newline at end of file diff --git a/docs/2.13.0/tls-connection/index.html b/docs/2.13.0/tls-connection/index.html index 179b0877e6..23056dd172 100644 --- a/docs/2.13.0/tls-connection/index.html +++ b/docs/2.13.0/tls-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie and OpenEdX over TLS for development | Richie - - + +
    -
    Version: 2.13.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 +

    Version: 2.13.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 and uses a fake LMS backend (base.BaseLMSBackend) as you can see if you check the RICHIE_LMS_BACKENDS setting in env.d/development.

    This base backend uses session storage to fake enrollments to course runs.

    If you want to test real enrollments to an OpenEdX instance hosted on an external domain, OpenEdX will need to generate a CORS CSRF Cookie. This cookie is flagged as secure, which implies that @@ -32,7 +32,7 @@ to share cookies with the OpenEdx application to allow CORS CSRF Protected XHR requests.

    4. Start Richie and OpenEdx over SSL

    Now, the OpenEdx application should respond on https://edx.local.dev:8073, and Richie on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. The next time you want to use SSL, you can run the following command on both the Richie and OpenEdX projects:

    $ make run-ssl

    Of course, you can still run apps without ssl by using:

    $ make run
    - - + + \ No newline at end of file diff --git a/docs/2.13.0/web-analytics/index.html b/docs/2.13.0/web-analytics/index.html index 0e040e379c..a51199f3aa 100644 --- a/docs/2.13.0/web-analytics/index.html +++ b/docs/2.13.0/web-analytics/index.html @@ -4,16 +4,16 @@ Add web analytics to your site | Richie - - + +
    -
    Version: 2.13.0

    Add web analytics to your site

    The purpose of this file is to document how you can enable a Web Analytics solution. +

    Version: 2.13.0

    Add web analytics to your site

    The purpose of this file is to document how you can enable a Web Analytics solution. Currently Richie only supports by default the Google Analytics using the Google Tag Manager Javascript. But it is possible with little cost to add support for other web analytics solutions.

    Google Analytics

    Next, it is decribed how you can configure the Google Analytics on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Analytics tracking id code.

    The current Google Analytics implementation also includes custom dimensions. Those dimensions permit you to create further analyses on Google Analytics or even use them to create custom reports. Custom dimensions with a value as example:

    • Organizations codes - UNIV_LISBON | UNIV_PORTO
    • Course code - COURSE_XPTO
    • Course runs titles - Summer edition | Winter edition
    • Course runs resource links - http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/info
    • Page title - Introduction to Programming

    Location of the web analytics javascript

    Use the WEB_ANALYTICS_LOCATION settings to decide where do you want to put the Javascript code. Use head (default value), to put the Javascript on HTML header, or footer, to put the Javascript code to the bottom of the body.

    Add a new Web Analytics solution

    In this section it's described how you can add support to a different Web Analytics solution.

    • override the richie/web_analytics.html template
    • define the WEB_ANALYTICS_ID setting with your tracking identification
    • define the WEB_ANALYTICS_PROVIDER setting with a value that represents your solution, eg. my-custom-web-analytics-software
    • optionally change WEB_ANALYTICS_LOCATION setting with head (default) or footer value
    • Example of a richie/web_analytics.html file customization that prints to the browser console log the dimension keys and values:
    <script type="text/javascript">
    {% for dimension_key, dimension_value_list in WEB_ANALYTICS.DIMENSIONS.items %}
    console.log("dimension: index '{{forloop.counter}}' with key '{{ dimension_key }}' with value '{{ dimension_value_list|join:" | " }}'");
    {% endfor %}
    </script>

    Output:

    dimension: index '1' with key 'organizations_codes' with value 'COMPATIBLE-EVEN-KEELED-UTILIZATION-19 | FOCUSED-NEXT-GENERATION-FUNCTIONALITIES-22 | UNIVERSAL-MODULAR-LOCAL-AREA-NETWORK-23'
    dimension: index '2' with key 'course_code' with value '00017'
    dimension: index '3' with key 'course_runs_titles' with value 'Run 0'
    dimension: index '4' with key 'course_runs_resource_links' with value ''
    dimension: index '5' with key 'page_title' with value 'Business-focused zero-defect application'

    But you can also contribute to Richie by creating a pull request to add support for a different web analytics solution. In this last case, you have to edit directly the richie/web_analytics.html template.

    Example of an override of the richie/web_analytics.html file:

    {% block web_analytics %}
    {% if WEB_ANALYTICS_ID %}
    {% if WEB_ANALYTICS_PROVIDER == "my_custom_web_analytics_software" %}
    <script type="text/javascript" src="{% static 'myapp/js/custom_web_analytics_software.js' %}">
    <script type="text/javascript">
    // javascript code that startups the custom web analytics software
    </script>
    {% endif %}
    {% endif %}
    {% endblock web_analytics %}

    The web analytics dimensions are being added to the django context using the WEB_ANALYTICS.DIMENSIONS dictionary. Because each dimension value could have multiple values, then each dictionary value is a list. Web analytics dimensions dictionary keys:

    • organizations_codes
    • course_code
    • course_runs_titles
    • course_runs_resource_links
    • page_title

    Example, if you only need the organization codes on your custom richie/web_analytics.html file:

    <script type="text/javascript">
    console.log("organization codes: '{{ WEB_ANALYTICS.DIMENSIONS.organizations_codes |join:" | " }}");
    </script>
    - - + + \ No newline at end of file diff --git a/docs/2.14.0/accessibility-testing/index.html b/docs/2.14.0/accessibility-testing/index.html index b17ed15cd9..9c14ad663c 100644 --- a/docs/2.14.0/accessibility-testing/index.html +++ b/docs/2.14.0/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.14.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.14.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.14.0/api/course-run-synchronization-api/index.html b/docs/2.14.0/api/course-run-synchronization-api/index.html index 0d169eb604..42843155a6 100644 --- a/docs/2.14.0/api/course-run-synchronization-api/index.html +++ b/docs/2.14.0/api/course-run-synchronization-api/index.html @@ -4,12 +4,12 @@ Course run synchronization API | Richie - - + +
    -
    Version: 2.14.0

    Course run synchronization API

    API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

    Synchronization endpoint [/api/1.0/course-runs-sync]

    This documentation describes version "1.0" of this API endpoint.

    Synchronize a course run [POST]

    It takes a JSON object containing the course run details:

    • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - +
      Version: 2.14.0

      Course run synchronization API

      API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

      Synchronization endpoint [/api/1.0/course-runs-sync]

      This documentation describes version "1.0" of this API endpoint.

      Synchronize a course run [POST]

      It takes a JSON object containing the course run details:

      • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - url of the course syllabus on the LMS from which a unique course identifier can be extracted
      • start: 2018-02-01T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course starts
      • end: 2018-02-28T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course ends
      • enrollment_start: 2018-01-01T06:00:00Z (string, optional) - ISO 8601 date, when enrollment @@ -19,7 +19,7 @@ containing the digest of the utf-8 encoded json representation of the submitted data for the given secret key and SHA256 digest algorithm (see [synchronizing-course-runs] for an example).
    • Body

        {
      "resource_link": "https://lms.example.com/courses/course-v1:001+001+001/info",
      "start": "2021-02-01T00:00:00Z",
      "end": "2021-02-31T23:59:59Z",
      "enrollment_start": "2021-01-01T00:00:00Z",
      "enrollment_end": "2021-01-31T23:59:59Z",
      "languages": ["en", "fr"]
      }
  • Response 200 (application/json)

    • Body

        {
      "success": True
      }
  • - - + + \ No newline at end of file diff --git a/docs/2.14.0/building-the-frontend/index.html b/docs/2.14.0/building-the-frontend/index.html index f3c729e45e..cfa4c1fe4b 100644 --- a/docs/2.14.0/building-the-frontend/index.html +++ b/docs/2.14.0/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.14.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.14.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.14.0/contributing-guide/index.html b/docs/2.14.0/contributing-guide/index.html index c132797471..78b68f90ad 100644 --- a/docs/2.14.0/contributing-guide/index.html +++ b/docs/2.14.0/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.14.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.14.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.14.0/css-guidelines/index.html b/docs/2.14.0/css-guidelines/index.html index 580d715755..8f889ae470 100644 --- a/docs/2.14.0/css-guidelines/index.html +++ b/docs/2.14.0/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.14.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.14.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.14.0/discover/index.html b/docs/2.14.0/discover/index.html index d93f5cfd29..58ce921da2 100644 --- a/docs/2.14.0/discover/index.html +++ b/docs/2.14.0/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
    -
    Version: 2.14.0

    Getting started with Richie

    If you're looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed +

    Version: 2.14.0

    Getting started with Richie

    If you're looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of 4 services:

    • db: the Postgresql database,
    • elasticsearch: the search engine,
    • app: the actual DjangoCMS project with all our application code,
    • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

    At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -24,7 +24,7 @@ a course on the LMS, and the LMS points back to the catalogue to browse courses.

    This approach is used for example on https://www.fun-campus.fr or https://catalogue.edulib.org.

    For a seamless user experience, it is possible to connect a Richie instance to an OpenEdX instance (or some other LMS like Moodle at the cost of minor adaptations), in several ways that we explain in the LMS connection guide.

    - - + + \ No newline at end of file diff --git a/docs/2.14.0/displaying-connection-status/index.html b/docs/2.14.0/displaying-connection-status/index.html index 54de82650b..e3a86f519c 100644 --- a/docs/2.14.0/displaying-connection-status/index.html +++ b/docs/2.14.0/displaying-connection-status/index.html @@ -4,12 +4,12 @@ Displaying OpenEdX connection status in Richie | Richie - - + +
    -
    Version: 2.14.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status +

    Version: 2.14.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status and display profile information for the logged-in user in Richie.

    In Richie, the only users that are actually authenticated on the DjangoCMS instance producing the site, are editors and staff users. Your instructors and learners will not have user accounts on Richie, but authentication is delegated to a remote service that can be OpenEdX, KeyCloack, or @@ -26,7 +26,7 @@ set for the above authentication delegation BASE_URL setting.

    If you need to bind user data into a url, wrap the property between brackets. For example, the link configured above for the profile page {base_url:s}/u/(username) would point to https://lms.example.com/u/johndoe for a user carrying the username johndoe.

    - - + + \ No newline at end of file diff --git a/docs/2.14.0/django-react-interop/index.html b/docs/2.14.0/django-react-interop/index.html index c0fc23ba33..5e85872415 100644 --- a/docs/2.14.0/django-react-interop/index.html +++ b/docs/2.14.0/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.14.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.14.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.14.0/docker-development/index.html b/docs/2.14.0/docker-development/index.html index 7c1a9cbc5c..94c0bf3653 100644 --- a/docs/2.14.0/docker-development/index.html +++ b/docs/2.14.0/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.14.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.14.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.14.0/frontend-overrides/index.html b/docs/2.14.0/frontend-overrides/index.html index 01699badfd..e854a1a3b6 100644 --- a/docs/2.14.0/frontend-overrides/index.html +++ b/docs/2.14.0/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.14.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.14.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.14.0/internationalization/index.html b/docs/2.14.0/internationalization/index.html index d6809c86f9..c4ce45153f 100644 --- a/docs/2.14.0/internationalization/index.html +++ b/docs/2.14.0/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.14.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.14.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.14.0/lms-backends/index.html b/docs/2.14.0/lms-backends/index.html index 14a9c67b90..04c5cb02b3 100644 --- a/docs/2.14.0/lms-backends/index.html +++ b/docs/2.14.0/lms-backends/index.html @@ -4,12 +4,12 @@ Configuring LMS Backends | Richie - - + +
    -
    Version: 2.14.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless +

    Version: 2.14.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless experience between browsing the course catalog on Richie, enrolling to a course and following the course itself on the LMS.

    It is possible to do the same with Moodle or any other LMS exposing an enrollment API, at the cost of writing a custom LMS handler backend.

    Prerequisites

    This connection requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that @@ -36,7 +36,7 @@ value other then manual.

    • Type: enum(string)
    • Required: No
    • Value: for example ["languages"]

    Technical support

    If you encounter an issue with this documentation or the backends included in Richie, please open an issue on our repository.

    If you need a custom backend, you can submit a PR or open an issue and we will consider adding it.

    - - + + \ No newline at end of file diff --git a/docs/2.14.0/lms-connection/index.html b/docs/2.14.0/lms-connection/index.html index d30c8979ef..8f53ee8f0c 100644 --- a/docs/2.14.0/lms-connection/index.html +++ b/docs/2.14.0/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with one or more LMS | Richie - - + +
    -
    Version: 2.14.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated +

    Version: 2.14.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated seamless experience.

    As of today, each approach has been implemented for OpenEdX but the same could be done for other LMSes like Moodle, at the cost of minor adaptations.

    1. Displaying connection status

    OpenEdX can be configured to allow CORS requests. Doing so allows Richie to retrieve a user's connection status from OpenEdx and display the user's profile information directly on the Richie @@ -31,7 +31,7 @@ docker-compose.

    If you want to activate seamless enrollment locally for development, you will need to set up TLS domains for both Richie and OpenEdX. To do this, head over to our guide on setting-up TLS connections for Richie and OpenEdX.

    - - + + \ No newline at end of file diff --git a/docs/2.14.0/native-installation/index.html b/docs/2.14.0/native-installation/index.html index c71ed3a23b..34d8fae173 100644 --- a/docs/2.14.0/native-installation/index.html +++ b/docs/2.14.0/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.14.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.14.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.14.0/synchronizing-course-runs/index.html b/docs/2.14.0/synchronizing-course-runs/index.html index 0ecd4603ab..a197a324b9 100644 --- a/docs/2.14.0/synchronizing-course-runs/index.html +++ b/docs/2.14.0/synchronizing-course-runs/index.html @@ -4,12 +4,12 @@ Synchronizing course runs between Richie and OpenEdX | Richie - - + +
    -
    Version: 2.14.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the +

    Version: 2.14.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the RICHIE_COURSE_RUN_SYNC_SECRETS setting with one or more secrets:

    RICHIE_COURSE_RUN_SYNC_SECRETS = ["SharedSecret", "OtherSharedSecret"]

    This setting collects several secrets in order to allow rotating them without any downtime. Any of the secrets listed in this setting can be used to sign your queries.

    Your secret should be shared with the LMS or distant system that needs to synchronize its course runs with the Richie instance. Richie will try the declared secrets one by one until it finds @@ -29,7 +29,7 @@ course run is modified and published.

    Or you can use the Richie Open edX Synchronization which is based on the following code sample and also includes the enrollment count.

    Given a COURSE_HOOK setting defined as follows in your OpenEdX instance:

    COURSE_HOOK = {
    "secret": "SharedSecret",
    "url": "https://richie.example.com/api/v1.0/course-runs-sync/",
    }

    The code for the synchronization function in OpenEdX could look like this:

    import hashlib
    import hmac
    import json

    from django.conf import settings

    from microsite_configuration import microsite
    import requests
    from xmodule.modulestore.django import modulestore


    def update_course(course_key, *args, **kwargs):
    """Synchronize an OpenEdX course, identified by its course key, with a Richie instance."""
    course = modulestore().get_course(course_key)
    edxapp_domain = microsite.get_value("site_domain", settings.LMS_BASE)

    data = {
    "resource_link": "https://{:s}/courses/{!s}/info".format(
    edxapp_domain, course_key
    ),
    "start": course.start and course.start.isoformat(),
    "end": course.end and course.end.isoformat(),
    "enrollment_start": course.enrollment_start and course.enrollment_start.isoformat(),
    "enrollment_end": course.enrollment_end and course.enrollment_end.isoformat(),
    "languages": [course.language or settings.LANGUAGE_CODE],
    }

    signature = hmac.new(
    setting.COURSE_HOOK["secret"].encode("utf-8"),
    msg=json.dumps(data).encode("utf-8"),
    digestmod=hashlib.sha256,
    ).hexdigest()

    response = requests.post(
    setting.COURSE_HOOK["url"],
    json=data,
    headers={"Authorization": "SIG-HMAC-SHA256 {:s}".format(signature)},
    )

    Thanks to the signal emitted in OpenEdX, this function can then be triggered each time a course is modified and published:

    from django.dispatch import receiver
    from xmodule.modulestore.django import SignalHandler


    @receiver(SignalHandler.course_published, dispatch_uid='update_course_on_publish')
    def update_course_on_publish(sender, course_key, **kwargs):
    update_course(course_key)
    - - + + \ No newline at end of file diff --git a/docs/2.14.0/tls-connection/index.html b/docs/2.14.0/tls-connection/index.html index 5ef8c7181c..aae990e878 100644 --- a/docs/2.14.0/tls-connection/index.html +++ b/docs/2.14.0/tls-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie and OpenEdX over TLS for development | Richie - - + +
    -
    Version: 2.14.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 +

    Version: 2.14.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 and uses a fake LMS backend (base.BaseLMSBackend) as you can see if you check the RICHIE_LMS_BACKENDS setting in env.d/development.

    This base backend uses session storage to fake enrollments to course runs.

    If you want to test real enrollments to an OpenEdX instance hosted on an external domain, OpenEdX will need to generate a CORS CSRF Cookie. This cookie is flagged as secure, which implies that @@ -32,7 +32,7 @@ to share cookies with the OpenEdx application to allow CORS CSRF Protected XHR requests.

    4. Start Richie and OpenEdx over SSL

    Now, the OpenEdx application should respond on https://edx.local.dev:8073, and Richie on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. The next time you want to use SSL, you can run the following command on both the Richie and OpenEdX projects:

    $ make run-ssl

    Of course, you can still run apps without ssl by using:

    $ make run
    - - + + \ No newline at end of file diff --git a/docs/2.14.0/web-analytics/index.html b/docs/2.14.0/web-analytics/index.html index 56445c6613..a5e6434fa0 100644 --- a/docs/2.14.0/web-analytics/index.html +++ b/docs/2.14.0/web-analytics/index.html @@ -4,19 +4,19 @@ Add web analytics to your site | Richie - - + +
    -
    Version: 2.14.0

    Add web analytics to your site

    Richie has native support to Google Analytics and Google Tag Manager Web Analytics solutions. +

    Version: 2.14.0

    Add web analytics to your site

    Richie has native support to Google Analytics and Google Tag Manager Web Analytics solutions. The purpose of this file is to explain how you can enable one of the supported Web Analytics providers and how you can extend Richie with an alternative solution.

    Google Analytics

    Next, it is described how you can configure the Google Analytics on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Analytics tracking id code.

    The current Google Analytics implementation also includes custom dimensions. Those dimensions permit you to create further analyses on Google Analytics or even use them to create custom reports. Custom dimensions with a value as example:

    • Organizations codes - UNIV_LISBON | UNIV_PORTO
    • Course code - COURSE_XPTO
    • Course runs titles - Summer edition | Winter edition
    • Course runs resource links - http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/info
    • Page title - Introduction to Programming

    Google Tag Manager

    Next, it is described how you can configure the Google Tag Manager on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Tag Manager tracking id code.
    • Add the WEB_ANALYTICS_PROVIDER setting with the google_tag_manager value.

    The current Google Tag Manager implementation also defines a custom dimensions like the Google Analytics.

    Location of the web analytics javascript

    Use the WEB_ANALYTICS_LOCATION settings to decide where do you want to put the Javascript code. Use head (default value), to put the Javascript on HTML header, or footer, to put the Javascript code to the bottom of the body.

    Add a new Web Analytics solution

    In this section it's described how you can add support to a different Web Analytics solution.

    • override the richie/web_analytics.html template
    • define the WEB_ANALYTICS_ID setting with your tracking identification
    • define the WEB_ANALYTICS_PROVIDER setting with a value that represents your solution, eg. my-custom-web-analytics-software
    • optionally change WEB_ANALYTICS_LOCATION setting with head (default) or footer value
    • Example of a richie/web_analytics.html file customization that prints to the browser console log the dimension keys and values:
    <script type="text/javascript">
    {% for dimension_key, dimension_value_list in WEB_ANALYTICS.DIMENSIONS.items %}
    console.log("dimension: index '{{forloop.counter}}' with key '{{ dimension_key }}' with value '{{ dimension_value_list|join:" | " }}'");
    {% endfor %}
    </script>

    Output:

    dimension: index '1' with key 'organizations_codes' with value 'COMPATIBLE-EVEN-KEELED-UTILIZATION-19 | FOCUSED-NEXT-GENERATION-FUNCTIONALITIES-22 | UNIVERSAL-MODULAR-LOCAL-AREA-NETWORK-23'
    dimension: index '2' with key 'course_code' with value '00017'
    dimension: index '3' with key 'course_runs_titles' with value 'Run 0'
    dimension: index '4' with key 'course_runs_resource_links' with value ''
    dimension: index '5' with key 'page_title' with value 'Business-focused zero-defect application'

    But you can also contribute to Richie by creating a pull request to add support for a different web analytics solution. In this last case, you have to edit directly the richie/web_analytics.html template.

    Example of an override of the richie/web_analytics.html file:

    {% block web_analytics %}
    {% if WEB_ANALYTICS_ID %}
    {% if WEB_ANALYTICS_PROVIDER == "my_custom_web_analytics_software" %}
    <script type="text/javascript" src="{% static 'myapp/js/custom_web_analytics_software.js' %}">
    <script type="text/javascript">
    // javascript code that startups the custom web analytics software
    </script>
    {% endif %}
    {% endif %}
    {% endblock web_analytics %}

    The web analytics dimensions are being added to the django context using the WEB_ANALYTICS.DIMENSIONS dictionary. Because each dimension value could have multiple values, then each dictionary value is a list. Web analytics dimensions dictionary keys:

    • organizations_codes
    • course_code
    • course_runs_titles
    • course_runs_resource_links
    • page_title

    Example, if you only need the organization codes on your custom richie/web_analytics.html file:

    <script type="text/javascript">
    console.log("organization codes: '{{ WEB_ANALYTICS.DIMENSIONS.organizations_codes |join:" | " }}");
    </script>

    The frontend code also sends events to the web analytics provider. Richie sends events when the user is enrolled on a course run. To support different providers, you need to create a similar file of src/frontend/js/utils/api/web-analytics/google_analytics.ts and change the src/frontend/js/utils/api/web-analytics/index.ts file to include that newer provider.

    - - + + \ No newline at end of file diff --git a/docs/2.14.1/accessibility-testing/index.html b/docs/2.14.1/accessibility-testing/index.html index 92f70326c7..db26c863eb 100644 --- a/docs/2.14.1/accessibility-testing/index.html +++ b/docs/2.14.1/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.14.1

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.14.1

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.14.1/api/course-run-synchronization-api/index.html b/docs/2.14.1/api/course-run-synchronization-api/index.html index 6f34e3b7fa..f3ceede90d 100644 --- a/docs/2.14.1/api/course-run-synchronization-api/index.html +++ b/docs/2.14.1/api/course-run-synchronization-api/index.html @@ -4,12 +4,12 @@ Course run synchronization API | Richie - - + +
    -
    Version: 2.14.1

    Course run synchronization API

    API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

    Synchronization endpoint [/api/1.0/course-runs-sync]

    This documentation describes version "1.0" of this API endpoint.

    Synchronize a course run [POST]

    It takes a JSON object containing the course run details:

    • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - +
      Version: 2.14.1

      Course run synchronization API

      API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

      Synchronization endpoint [/api/1.0/course-runs-sync]

      This documentation describes version "1.0" of this API endpoint.

      Synchronize a course run [POST]

      It takes a JSON object containing the course run details:

      • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - url of the course syllabus on the LMS from which a unique course identifier can be extracted
      • start: 2018-02-01T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course starts
      • end: 2018-02-28T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course ends
      • enrollment_start: 2018-01-01T06:00:00Z (string, optional) - ISO 8601 date, when enrollment @@ -19,7 +19,7 @@ containing the digest of the utf-8 encoded json representation of the submitted data for the given secret key and SHA256 digest algorithm (see [synchronizing-course-runs] for an example).
    • Body

        {
      "resource_link": "https://lms.example.com/courses/course-v1:001+001+001/info",
      "start": "2021-02-01T00:00:00Z",
      "end": "2021-02-31T23:59:59Z",
      "enrollment_start": "2021-01-01T00:00:00Z",
      "enrollment_end": "2021-01-31T23:59:59Z",
      "languages": ["en", "fr"]
      }
  • Response 200 (application/json)

    • Body

        {
      "success": True
      }
  • - - + + \ No newline at end of file diff --git a/docs/2.14.1/building-the-frontend/index.html b/docs/2.14.1/building-the-frontend/index.html index ad449bf361..ec0af064c4 100644 --- a/docs/2.14.1/building-the-frontend/index.html +++ b/docs/2.14.1/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.14.1

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.14.1

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.14.1/contributing-guide/index.html b/docs/2.14.1/contributing-guide/index.html index 432e338889..90c932e4ca 100644 --- a/docs/2.14.1/contributing-guide/index.html +++ b/docs/2.14.1/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.14.1

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.14.1

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.14.1/cookiecutter/index.html b/docs/2.14.1/cookiecutter/index.html index bac85f035b..5c6ba41613 100644 --- a/docs/2.14.1/cookiecutter/index.html +++ b/docs/2.14.1/cookiecutter/index.html @@ -4,12 +4,12 @@ Start your own site | Richie - - + +
    -
    Version: 2.14.1

    Start your own site

    We use Cookiecutter to help you +

    Version: 2.14.1

    Start your own site

    We use Cookiecutter to help you set up a production-ready learning portal website based on Richie in seconds.

    Run Cookiecutter

    There are 2 options to run Cookiecutter:

    While you think of it, navigate to the directory in which you want to create your site factory:

    cd /path/to/your/code/directory

    If you chose to install Cookiecutter, you can now run it against our @@ -30,7 +30,7 @@ project's scaffolding but, don't worry, it will respect all the sites you will have created in the sites directory:

    cookiecutter --overwrite-if-exists gh:openfun/richie --directory=cookiecutter

    Help us improve this project

    After starting your project, please submit an issue let us know how it went and what other features we should add to make it better.

    - - + + \ No newline at end of file diff --git a/docs/2.14.1/css-guidelines/index.html b/docs/2.14.1/css-guidelines/index.html index 3c91e273c9..c9f6580890 100644 --- a/docs/2.14.1/css-guidelines/index.html +++ b/docs/2.14.1/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.14.1

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.14.1

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.14.1/discover/index.html b/docs/2.14.1/discover/index.html index b713a93a68..1add846f8f 100644 --- a/docs/2.14.1/discover/index.html +++ b/docs/2.14.1/discover/index.html @@ -4,12 +4,12 @@ Discover Richie | Richie - - + +
    -
    Version: 2.14.1

    Discover Richie

    Learning Management Systems (LMS) are great tools for hosting and playing interactive online +

    Version: 2.14.1

    Discover Richie

    Learning Management Systems (LMS) are great tools for hosting and playing interactive online courses and MOOCs.

    However, if you need to build a complete website with flexible content to aggregate your courses, in several languages and from different sources, you will soon need a CMS.

    At "France Université Numérique", we wanted to build an OpenSource portal with Python and Django. That's why we built Richie on top of DjangoCMS, one of @@ -21,7 +21,7 @@ OpenEdX Docker.

    Two users are available for testing:

    • admin: admin@example.com/admin
    • student: edx@example.com/edx

    The database is regularly flushed.

    Start your own site

    The next step after discovering Richie on the demo is to start your own project. We provide a production-ready template based on Cookiecutter that allows you to generate your project in seconds.

    Follow the guide on starting your own Richie site with Cookiecutter.

    Once you created a new site, you will be able to fully customize it:

    - - + + \ No newline at end of file diff --git a/docs/2.14.1/displaying-connection-status/index.html b/docs/2.14.1/displaying-connection-status/index.html index 74c00f357b..6694a04857 100644 --- a/docs/2.14.1/displaying-connection-status/index.html +++ b/docs/2.14.1/displaying-connection-status/index.html @@ -4,12 +4,12 @@ Displaying OpenEdX connection status in Richie | Richie - - + +
    -
    Version: 2.14.1

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status +

    Version: 2.14.1

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status and display profile information for the logged-in user in Richie.

    In Richie, the only users that are actually authenticated on the DjangoCMS instance producing the site, are editors and staff users. Your instructors and learners will not have user accounts on Richie, but authentication is delegated to a remote service that can be OpenEdX, KeyCloack, or @@ -26,7 +26,7 @@ set for the above authentication delegation BASE_URL setting.

    If you need to bind user data into a url, wrap the property between brackets. For example, the link configured above for the profile page {base_url:s}/u/(username) would point to https://lms.example.com/u/johndoe for a user carrying the username johndoe.

    - - + + \ No newline at end of file diff --git a/docs/2.14.1/django-react-interop/index.html b/docs/2.14.1/django-react-interop/index.html index 6acc28ec46..9bea6c42a2 100644 --- a/docs/2.14.1/django-react-interop/index.html +++ b/docs/2.14.1/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.14.1

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.14.1

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.14.1/docker-development/index.html b/docs/2.14.1/docker-development/index.html index cd4f134d60..1d1f3df7c2 100644 --- a/docs/2.14.1/docker-development/index.html +++ b/docs/2.14.1/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.14.1

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.14.1

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.14.1/frontend-overrides/index.html b/docs/2.14.1/frontend-overrides/index.html index 9634b7ea2a..ecfe1e760a 100644 --- a/docs/2.14.1/frontend-overrides/index.html +++ b/docs/2.14.1/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.14.1

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.14.1

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.14.1/installation/index.html b/docs/2.14.1/installation/index.html index 76e4382d95..6a8d9abc70 100644 --- a/docs/2.14.1/installation/index.html +++ b/docs/2.14.1/installation/index.html @@ -4,12 +4,12 @@ Installing Richie for development | Richie - - + +
    -
    Version: 2.14.1

    Installing Richie for development

    Richie is a container-native application but can also be installed +

    Version: 2.14.1

    Installing Richie for development

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of:

    • 3 running services:

      • database: postgresql or mysql at your preference,
      • elasticsearch: the search engine,
      • app: the actual DjangoCMS project with all our application code.
    • 2 containers for building purposes:

    - - + + \ No newline at end of file diff --git a/docs/2.14.1/internationalization/index.html b/docs/2.14.1/internationalization/index.html index a2385d885a..71910ebf2d 100644 --- a/docs/2.14.1/internationalization/index.html +++ b/docs/2.14.1/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.14.1

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.14.1

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.14.1/joanie-connection/index.html b/docs/2.14.1/joanie-connection/index.html index b18a930dd4..e90a90de54 100644 --- a/docs/2.14.1/joanie-connection/index.html +++ b/docs/2.14.1/joanie-connection/index.html @@ -4,12 +4,12 @@ Joanie Connection | Richie - - + +
    -
    Version: 2.14.1

    Joanie Connection

    Settings

    All settings related to Joanie have to be declared in the JOANIE dictionary +

    Version: 2.14.1

    Joanie Connection

    Settings

    All settings related to Joanie have to be declared in the JOANIE dictionary within settings.py. To enable Joanie, the minimal configuration requires one property:

    • BASE_URL : the endpoint at which Joanie is accessible

    Add to your settings.py:

    ...
    JOANIE = {
    "BASE_URL": values.Value(environ_name="JOANIE_BASE_URL", environ_prefix=None)
    }
    ...

    Access Token

    Lifetime configuration

    Access Token is stored within the SessionStorage through react-query client persistor. @@ -19,7 +19,7 @@ lifetime of the access token defined by your authentication server. For example, if your authentication server is using djangorestframework-simplejwt to generate the access token, its lifetime is 5 minutes by default.

    - - + + \ No newline at end of file diff --git a/docs/2.14.1/lms-backends/index.html b/docs/2.14.1/lms-backends/index.html index 0b527ed630..c22e59b523 100644 --- a/docs/2.14.1/lms-backends/index.html +++ b/docs/2.14.1/lms-backends/index.html @@ -4,12 +4,12 @@ Configuring LMS Backends | Richie - - + +
    -
    Version: 2.14.1

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless +

    Version: 2.14.1

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless experience between browsing the course catalog on Richie, enrolling to a course and following the course itself on the LMS.

    It is possible to do the same with Moodle or any other LMS exposing an enrollment API, at the cost of writing a custom LMS handler backend.

    Prerequisites

    This connection requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that @@ -36,7 +36,7 @@ value other then manual.

    • Type: enum(string)
    • Required: No
    • Value: for example ["languages"]

    Technical support

    If you encounter an issue with this documentation or the backends included in Richie, please open an issue on our repository.

    If you need a custom backend, you can submit a PR or open an issue and we will consider adding it.

    - - + + \ No newline at end of file diff --git a/docs/2.14.1/lms-connection/index.html b/docs/2.14.1/lms-connection/index.html index 4185775555..7b2dba34c5 100644 --- a/docs/2.14.1/lms-connection/index.html +++ b/docs/2.14.1/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with one or more LMS | Richie - - + +
    -
    Version: 2.14.1

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated +

    Version: 2.14.1

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated seamless experience.

    As of today, each approach has been implemented for OpenEdX but the same could be done for other LMSes like Moodle, at the cost of minor adaptations.

    1. Displaying connection status

    OpenEdX can be configured to allow CORS requests. Doing so allows Richie to retrieve a user's connection status from OpenEdx and display the user's profile information directly on the Richie @@ -31,7 +31,7 @@ docker-compose.

    If you want to activate seamless enrollment locally for development, you will need to set up TLS domains for both Richie and OpenEdX. To do this, head over to our guide on setting-up TLS connections for Richie and OpenEdX.

    - - + + \ No newline at end of file diff --git a/docs/2.14.1/native-installation/index.html b/docs/2.14.1/native-installation/index.html index 22d1d6513b..4dff46d4dc 100644 --- a/docs/2.14.1/native-installation/index.html +++ b/docs/2.14.1/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.14.1

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.14.1

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native installation instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.14.1/synchronizing-course-runs/index.html b/docs/2.14.1/synchronizing-course-runs/index.html index 8ae8b5ec31..e99c10dfdf 100644 --- a/docs/2.14.1/synchronizing-course-runs/index.html +++ b/docs/2.14.1/synchronizing-course-runs/index.html @@ -4,12 +4,12 @@ Synchronizing course runs between Richie and OpenEdX | Richie - - + +
    -
    Version: 2.14.1

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the +

    Version: 2.14.1

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the RICHIE_COURSE_RUN_SYNC_SECRETS setting with one or more secrets:

    RICHIE_COURSE_RUN_SYNC_SECRETS = ["SharedSecret", "OtherSharedSecret"]

    This setting collects several secrets in order to allow rotating them without any downtime. Any of the secrets listed in this setting can be used to sign your queries.

    Your secret should be shared with the LMS or distant system that needs to synchronize its course runs with the Richie instance. Richie will try the declared secrets one by one until it finds @@ -29,7 +29,7 @@ course run is modified and published.

    Or you can use the Richie Open edX Synchronization which is based on the following code sample and also includes the enrollment count.

    Given a COURSE_HOOK setting defined as follows in your OpenEdX instance:

    COURSE_HOOK = {
    "secret": "SharedSecret",
    "url": "https://richie.example.com/api/v1.0/course-runs-sync/",
    }

    The code for the synchronization function in OpenEdX could look like this:

    import hashlib
    import hmac
    import json

    from django.conf import settings

    from microsite_configuration import microsite
    import requests
    from xmodule.modulestore.django import modulestore


    def update_course(course_key, *args, **kwargs):
    """Synchronize an OpenEdX course, identified by its course key, with a Richie instance."""
    course = modulestore().get_course(course_key)
    edxapp_domain = microsite.get_value("site_domain", settings.LMS_BASE)

    data = {
    "resource_link": "https://{:s}/courses/{!s}/info".format(
    edxapp_domain, course_key
    ),
    "start": course.start and course.start.isoformat(),
    "end": course.end and course.end.isoformat(),
    "enrollment_start": course.enrollment_start and course.enrollment_start.isoformat(),
    "enrollment_end": course.enrollment_end and course.enrollment_end.isoformat(),
    "languages": [course.language or settings.LANGUAGE_CODE],
    }

    signature = hmac.new(
    setting.COURSE_HOOK["secret"].encode("utf-8"),
    msg=json.dumps(data).encode("utf-8"),
    digestmod=hashlib.sha256,
    ).hexdigest()

    response = requests.post(
    setting.COURSE_HOOK["url"],
    json=data,
    headers={"Authorization": "SIG-HMAC-SHA256 {:s}".format(signature)},
    )

    Thanks to the signal emitted in OpenEdX, this function can then be triggered each time a course is modified and published:

    from django.dispatch import receiver
    from xmodule.modulestore.django import SignalHandler


    @receiver(SignalHandler.course_published, dispatch_uid='update_course_on_publish')
    def update_course_on_publish(sender, course_key, **kwargs):
    update_course(course_key)
    - - + + \ No newline at end of file diff --git a/docs/2.14.1/tls-connection/index.html b/docs/2.14.1/tls-connection/index.html index 774bd5d9e6..223b2a229b 100644 --- a/docs/2.14.1/tls-connection/index.html +++ b/docs/2.14.1/tls-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie and OpenEdX over TLS for development | Richie - - + +
    -
    Version: 2.14.1

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 +

    Version: 2.14.1

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 and uses a fake LMS backend (base.BaseLMSBackend) as you can see if you check the RICHIE_LMS_BACKENDS setting in env.d/development.

    This base backend uses session storage to fake enrollments to course runs.

    If you want to test real enrollments to an OpenEdX instance hosted on an external domain, OpenEdX will need to generate a CORS CSRF Cookie. This cookie is flagged as secure, which implies that @@ -32,7 +32,7 @@ to share cookies with the OpenEdx application to allow CORS CSRF Protected XHR requests.

    4. Start Richie and OpenEdx over SSL

    Now, the OpenEdx application should respond on https://edx.local.dev:8073, and Richie on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. The next time you want to use SSL, you can run the following command on both the Richie and OpenEdX projects:

    $ make run-ssl

    Of course, you can still run apps without ssl by using:

    $ make run
    - - + + \ No newline at end of file diff --git a/docs/2.14.1/web-analytics/index.html b/docs/2.14.1/web-analytics/index.html index 5b64a75e83..735c53b138 100644 --- a/docs/2.14.1/web-analytics/index.html +++ b/docs/2.14.1/web-analytics/index.html @@ -4,19 +4,19 @@ Add web analytics to your site | Richie - - + +
    -
    Version: 2.14.1

    Add web analytics to your site

    Richie has native support to Google Analytics and Google Tag Manager Web Analytics solutions. +

    Version: 2.14.1

    Add web analytics to your site

    Richie has native support to Google Analytics and Google Tag Manager Web Analytics solutions. The purpose of this file is to explain how you can enable one of the supported Web Analytics providers and how you can extend Richie with an alternative solution.

    Google Analytics

    Next, it is described how you can configure the Google Analytics on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Analytics tracking id code.

    The current Google Analytics implementation also includes custom dimensions. Those dimensions permit you to create further analyses on Google Analytics or even use them to create custom reports. Custom dimensions with a value as example:

    • Organizations codes - UNIV_LISBON | UNIV_PORTO
    • Course code - COURSE_XPTO
    • Course runs titles - Summer edition | Winter edition
    • Course runs resource links - http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/info
    • Page title - Introduction to Programming

    Google Tag Manager

    Next, it is described how you can configure the Google Tag Manager on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Tag Manager tracking id code.
    • Add the WEB_ANALYTICS_PROVIDER setting with the google_tag_manager value.

    The current Google Tag Manager implementation also defines a custom dimensions like the Google Analytics.

    Location of the web analytics javascript

    Use the WEB_ANALYTICS_LOCATION settings to decide where do you want to put the Javascript code. Use head (default value), to put the Javascript on HTML header, or footer, to put the Javascript code to the bottom of the body.

    Add a new Web Analytics solution

    In this section it's described how you can add support to a different Web Analytics solution.

    • override the richie/web_analytics.html template
    • define the WEB_ANALYTICS_ID setting with your tracking identification
    • define the WEB_ANALYTICS_PROVIDER setting with a value that represents your solution, eg. my-custom-web-analytics-software
    • optionally change WEB_ANALYTICS_LOCATION setting with head (default) or footer value
    • Example of a richie/web_analytics.html file customization that prints to the browser console log the dimension keys and values:
    <script type="text/javascript">
    {% for dimension_key, dimension_value_list in WEB_ANALYTICS.DIMENSIONS.items %}
    console.log("dimension: index '{{forloop.counter}}' with key '{{ dimension_key }}' with value '{{ dimension_value_list|join:" | " }}'");
    {% endfor %}
    </script>

    Output:

    dimension: index '1' with key 'organizations_codes' with value 'COMPATIBLE-EVEN-KEELED-UTILIZATION-19 | FOCUSED-NEXT-GENERATION-FUNCTIONALITIES-22 | UNIVERSAL-MODULAR-LOCAL-AREA-NETWORK-23'
    dimension: index '2' with key 'course_code' with value '00017'
    dimension: index '3' with key 'course_runs_titles' with value 'Run 0'
    dimension: index '4' with key 'course_runs_resource_links' with value ''
    dimension: index '5' with key 'page_title' with value 'Business-focused zero-defect application'

    But you can also contribute to Richie by creating a pull request to add support for a different web analytics solution. In this last case, you have to edit directly the richie/web_analytics.html template.

    Example of an override of the richie/web_analytics.html file:

    {% block web_analytics %}
    {% if WEB_ANALYTICS_ID %}
    {% if WEB_ANALYTICS_PROVIDER == "my_custom_web_analytics_software" %}
    <script type="text/javascript" src="{% static 'myapp/js/custom_web_analytics_software.js' %}">
    <script type="text/javascript">
    // javascript code that startups the custom web analytics software
    </script>
    {% endif %}
    {% endif %}
    {% endblock web_analytics %}

    The web analytics dimensions are being added to the django context using the WEB_ANALYTICS.DIMENSIONS dictionary. Because each dimension value could have multiple values, then each dictionary value is a list. Web analytics dimensions dictionary keys:

    • organizations_codes
    • course_code
    • course_runs_titles
    • course_runs_resource_links
    • page_title

    Example, if you only need the organization codes on your custom richie/web_analytics.html file:

    <script type="text/javascript">
    console.log("organization codes: '{{ WEB_ANALYTICS.DIMENSIONS.organizations_codes |join:" | " }}");
    </script>

    The frontend code also sends events to the web analytics provider. Richie sends events when the user is enrolled on a course run. To support different providers, you need to create a similar file of src/frontend/js/utils/api/web-analytics/google_analytics.ts and change the src/frontend/js/utils/api/web-analytics/index.ts file to include that newer provider.

    - - + + \ No newline at end of file diff --git a/docs/2.15.0/accessibility-testing/index.html b/docs/2.15.0/accessibility-testing/index.html index 8a10ddee84..201ed9da8e 100644 --- a/docs/2.15.0/accessibility-testing/index.html +++ b/docs/2.15.0/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.15.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.15.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.15.0/api/course-run-synchronization-api/index.html b/docs/2.15.0/api/course-run-synchronization-api/index.html index d19b812e27..f29e5938a3 100644 --- a/docs/2.15.0/api/course-run-synchronization-api/index.html +++ b/docs/2.15.0/api/course-run-synchronization-api/index.html @@ -4,12 +4,12 @@ Course run synchronization API | Richie - - + +
    -
    Version: 2.15.0

    Course run synchronization API

    API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

    Synchronization endpoint [/api/1.0/course-runs-sync]

    This documentation describes version "1.0" of this API endpoint.

    Synchronize a course run [POST]

    It takes a JSON object containing the course run details:

    • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - +
      Version: 2.15.0

      Course run synchronization API

      API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

      Synchronization endpoint [/api/1.0/course-runs-sync]

      This documentation describes version "1.0" of this API endpoint.

      Synchronize a course run [POST]

      It takes a JSON object containing the course run details:

      • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - url of the course syllabus on the LMS from which a unique course identifier can be extracted
      • start: 2018-02-01T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course starts
      • end: 2018-02-28T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course ends
      • enrollment_start: 2018-01-01T06:00:00Z (string, optional) - ISO 8601 date, when enrollment @@ -19,7 +19,7 @@ containing the digest of the utf-8 encoded json representation of the submitted data for the given secret key and SHA256 digest algorithm (see [synchronizing-course-runs] for an example).
    • Body

        {
      "resource_link": "https://lms.example.com/courses/course-v1:001+001+001/info",
      "start": "2021-02-01T00:00:00Z",
      "end": "2021-02-31T23:59:59Z",
      "enrollment_start": "2021-01-01T00:00:00Z",
      "enrollment_end": "2021-01-31T23:59:59Z",
      "languages": ["en", "fr"]
      }
  • Response 200 (application/json)

    • Body

        {
      "success": True
      }
  • - - + + \ No newline at end of file diff --git a/docs/2.15.0/building-the-frontend/index.html b/docs/2.15.0/building-the-frontend/index.html index c16110b188..67a9358f60 100644 --- a/docs/2.15.0/building-the-frontend/index.html +++ b/docs/2.15.0/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.15.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.15.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.15.0/contributing-guide/index.html b/docs/2.15.0/contributing-guide/index.html index e280bd58c7..63b297dc90 100644 --- a/docs/2.15.0/contributing-guide/index.html +++ b/docs/2.15.0/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.15.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.15.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.15.0/cookiecutter/index.html b/docs/2.15.0/cookiecutter/index.html index ad361a66ae..e04e16724e 100644 --- a/docs/2.15.0/cookiecutter/index.html +++ b/docs/2.15.0/cookiecutter/index.html @@ -4,12 +4,12 @@ Start your own site | Richie - - + +
    -
    Version: 2.15.0

    Start your own site

    We use Cookiecutter to help you +

    Version: 2.15.0

    Start your own site

    We use Cookiecutter to help you set up a production-ready learning portal website based on Richie in seconds.

    Run Cookiecutter

    There are 2 options to run Cookiecutter:

    While you think of it, navigate to the directory in which you want to create your site factory:

    cd /path/to/your/code/directory

    If you chose to install Cookiecutter, you can now run it against our @@ -30,7 +30,7 @@ project's scaffolding but, don't worry, it will respect all the sites you will have created in the sites directory:

    cookiecutter --overwrite-if-exists gh:openfun/richie --directory=cookiecutter

    Help us improve this project

    After starting your project, please submit an issue let us know how it went and what other features we should add to make it better.

    - - + + \ No newline at end of file diff --git a/docs/2.15.0/css-guidelines/index.html b/docs/2.15.0/css-guidelines/index.html index 6174eb0eb5..b8cd754f72 100644 --- a/docs/2.15.0/css-guidelines/index.html +++ b/docs/2.15.0/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.15.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.15.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.15.0/discover/index.html b/docs/2.15.0/discover/index.html index 1f7465cc97..8bb7e46dc0 100644 --- a/docs/2.15.0/discover/index.html +++ b/docs/2.15.0/discover/index.html @@ -4,12 +4,12 @@ Discover Richie | Richie - - + +
    -
    Version: 2.15.0

    Discover Richie

    Learning Management Systems (LMS) are great tools for hosting and playing interactive online +

    Version: 2.15.0

    Discover Richie

    Learning Management Systems (LMS) are great tools for hosting and playing interactive online courses and MOOCs.

    However, if you need to build a complete website with flexible content to aggregate your courses, in several languages and from different sources, you will soon need a CMS.

    At "France Université Numérique", we wanted to build an OpenSource portal with Python and Django. That's why we built Richie on top of DjangoCMS, one of @@ -21,7 +21,7 @@ OpenEdX Docker.

    Two users are available for testing:

    • admin: admin@example.com/admin
    • student: edx@example.com/edx

    The database is regularly flushed.

    Start your own site

    The next step after discovering Richie on the demo is to start your own project. We provide a production-ready template based on Cookiecutter that allows you to generate your project in seconds.

    Follow the guide on starting your own Richie site with Cookiecutter.

    Once you created a new site, you will be able to fully customize it:

    - - + + \ No newline at end of file diff --git a/docs/2.15.0/displaying-connection-status/index.html b/docs/2.15.0/displaying-connection-status/index.html index 57ab7e439a..322ec95263 100644 --- a/docs/2.15.0/displaying-connection-status/index.html +++ b/docs/2.15.0/displaying-connection-status/index.html @@ -4,12 +4,12 @@ Displaying OpenEdX connection status in Richie | Richie - - + +
    -
    Version: 2.15.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status +

    Version: 2.15.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status and display profile information for the logged-in user in Richie.

    In Richie, the only users that are actually authenticated on the DjangoCMS instance producing the site, are editors and staff users. Your instructors and learners will not have user accounts on Richie, but authentication is delegated to a remote service that can be OpenEdX, KeyCloack, or @@ -26,7 +26,7 @@ set for the above authentication delegation BASE_URL setting.

    If you need to bind user data into a url, wrap the property between brackets. For example, the link configured above for the profile page {base_url:s}/u/(username) would point to https://lms.example.com/u/johndoe for a user carrying the username johndoe.

    - - + + \ No newline at end of file diff --git a/docs/2.15.0/django-react-interop/index.html b/docs/2.15.0/django-react-interop/index.html index e846033d55..3e24fcdd88 100644 --- a/docs/2.15.0/django-react-interop/index.html +++ b/docs/2.15.0/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.15.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.15.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.15.0/docker-development/index.html b/docs/2.15.0/docker-development/index.html index ee1c252e34..74beb3b39d 100644 --- a/docs/2.15.0/docker-development/index.html +++ b/docs/2.15.0/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.15.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.15.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.15.0/frontend-overrides/index.html b/docs/2.15.0/frontend-overrides/index.html index 0abeaa4349..c306d8aab8 100644 --- a/docs/2.15.0/frontend-overrides/index.html +++ b/docs/2.15.0/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.15.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.15.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.15.0/installation/index.html b/docs/2.15.0/installation/index.html index e5126b7dee..7bef7a0311 100644 --- a/docs/2.15.0/installation/index.html +++ b/docs/2.15.0/installation/index.html @@ -4,12 +4,12 @@ Installing Richie for development | Richie - - + +
    -
    Version: 2.15.0

    Installing Richie for development

    Richie is a container-native application but can also be installed +

    Version: 2.15.0

    Installing Richie for development

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of:

    • 3 running services:

      • database: postgresql or mysql at your preference,
      • elasticsearch: the search engine,
      • app: the actual DjangoCMS project with all our application code.
    • 2 containers for building purposes:

    - - + + \ No newline at end of file diff --git a/docs/2.15.0/internationalization/index.html b/docs/2.15.0/internationalization/index.html index 04ac2e5bc7..10de964002 100644 --- a/docs/2.15.0/internationalization/index.html +++ b/docs/2.15.0/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.15.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.15.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.15.0/joanie-connection/index.html b/docs/2.15.0/joanie-connection/index.html index 1247f0fb0d..4f775f220a 100644 --- a/docs/2.15.0/joanie-connection/index.html +++ b/docs/2.15.0/joanie-connection/index.html @@ -4,12 +4,12 @@ Joanie Connection | Richie - - + +
    -
    Version: 2.15.0

    Joanie Connection

    Settings

    All settings related to Joanie have to be declared in the JOANIE dictionary +

    Version: 2.15.0

    Joanie Connection

    Settings

    All settings related to Joanie have to be declared in the JOANIE dictionary within settings.py. To enable Joanie, the minimal configuration requires one property:

    • BASE_URL : the endpoint at which Joanie is accessible

    Add to your settings.py:

    ...
    JOANIE = {
    "BASE_URL": values.Value(environ_name="JOANIE_BASE_URL", environ_prefix=None)
    }
    ...

    Access Token

    Lifetime configuration

    Access Token is stored within the SessionStorage through react-query client persistor. @@ -19,7 +19,7 @@ lifetime of the access token defined by your authentication server. For example, if your authentication server is using djangorestframework-simplejwt to generate the access token, its lifetime is 5 minutes by default.

    - - + + \ No newline at end of file diff --git a/docs/2.15.0/lms-backends/index.html b/docs/2.15.0/lms-backends/index.html index ae726c4dcb..66fa23402c 100644 --- a/docs/2.15.0/lms-backends/index.html +++ b/docs/2.15.0/lms-backends/index.html @@ -4,12 +4,12 @@ Configuring LMS Backends | Richie - - + +
    -
    Version: 2.15.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless +

    Version: 2.15.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless experience between browsing the course catalog on Richie, enrolling to a course and following the course itself on the LMS.

    It is possible to do the same with Moodle or any other LMS exposing an enrollment API, at the cost of writing a custom LMS handler backend.

    Prerequisites

    This connection requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that @@ -36,7 +36,7 @@ value other then manual.

    • Type: enum(string)
    • Required: No
    • Value: for example ["languages"]

    Technical support

    If you encounter an issue with this documentation or the backends included in Richie, please open an issue on our repository.

    If you need a custom backend, you can submit a PR or open an issue and we will consider adding it.

    - - + + \ No newline at end of file diff --git a/docs/2.15.0/lms-connection/index.html b/docs/2.15.0/lms-connection/index.html index 8709202caa..86156b49ac 100644 --- a/docs/2.15.0/lms-connection/index.html +++ b/docs/2.15.0/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with one or more LMS | Richie - - + +
    -
    Version: 2.15.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated +

    Version: 2.15.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated seamless experience.

    As of today, each approach has been implemented for OpenEdX but the same could be done for other LMSes like Moodle, at the cost of minor adaptations.

    1. Displaying connection status

    OpenEdX can be configured to allow CORS requests. Doing so allows Richie to retrieve a user's connection status from OpenEdx and display the user's profile information directly on the Richie @@ -31,7 +31,7 @@ docker-compose.

    If you want to activate seamless enrollment locally for development, you will need to set up TLS domains for both Richie and OpenEdX. To do this, head over to our guide on setting-up TLS connections for Richie and OpenEdX.

    - - + + \ No newline at end of file diff --git a/docs/2.15.0/native-installation/index.html b/docs/2.15.0/native-installation/index.html index 8363195143..822c9fc1dd 100644 --- a/docs/2.15.0/native-installation/index.html +++ b/docs/2.15.0/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.15.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.15.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native installation instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.15.0/synchronizing-course-runs/index.html b/docs/2.15.0/synchronizing-course-runs/index.html index 4aa163fae6..c5028c8785 100644 --- a/docs/2.15.0/synchronizing-course-runs/index.html +++ b/docs/2.15.0/synchronizing-course-runs/index.html @@ -4,12 +4,12 @@ Synchronizing course runs between Richie and OpenEdX | Richie - - + +
    -
    Version: 2.15.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the +

    Version: 2.15.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the RICHIE_COURSE_RUN_SYNC_SECRETS setting with one or more secrets:

    RICHIE_COURSE_RUN_SYNC_SECRETS = ["SharedSecret", "OtherSharedSecret"]

    This setting collects several secrets in order to allow rotating them without any downtime. Any of the secrets listed in this setting can be used to sign your queries.

    Your secret should be shared with the LMS or distant system that needs to synchronize its course runs with the Richie instance. Richie will try the declared secrets one by one until it finds @@ -29,7 +29,7 @@ course run is modified and published.

    Or you can use the Richie Open edX Synchronization which is based on the following code sample and also includes the enrollment count.

    Given a COURSE_HOOK setting defined as follows in your OpenEdX instance:

    COURSE_HOOK = {
    "secret": "SharedSecret",
    "url": "https://richie.example.com/api/v1.0/course-runs-sync/",
    }

    The code for the synchronization function in OpenEdX could look like this:

    import hashlib
    import hmac
    import json

    from django.conf import settings

    from microsite_configuration import microsite
    import requests
    from xmodule.modulestore.django import modulestore


    def update_course(course_key, *args, **kwargs):
    """Synchronize an OpenEdX course, identified by its course key, with a Richie instance."""
    course = modulestore().get_course(course_key)
    edxapp_domain = microsite.get_value("site_domain", settings.LMS_BASE)

    data = {
    "resource_link": "https://{:s}/courses/{!s}/info".format(
    edxapp_domain, course_key
    ),
    "start": course.start and course.start.isoformat(),
    "end": course.end and course.end.isoformat(),
    "enrollment_start": course.enrollment_start and course.enrollment_start.isoformat(),
    "enrollment_end": course.enrollment_end and course.enrollment_end.isoformat(),
    "languages": [course.language or settings.LANGUAGE_CODE],
    }

    signature = hmac.new(
    setting.COURSE_HOOK["secret"].encode("utf-8"),
    msg=json.dumps(data).encode("utf-8"),
    digestmod=hashlib.sha256,
    ).hexdigest()

    response = requests.post(
    setting.COURSE_HOOK["url"],
    json=data,
    headers={"Authorization": "SIG-HMAC-SHA256 {:s}".format(signature)},
    )

    Thanks to the signal emitted in OpenEdX, this function can then be triggered each time a course is modified and published:

    from django.dispatch import receiver
    from xmodule.modulestore.django import SignalHandler


    @receiver(SignalHandler.course_published, dispatch_uid='update_course_on_publish')
    def update_course_on_publish(sender, course_key, **kwargs):
    update_course(course_key)
    - - + + \ No newline at end of file diff --git a/docs/2.15.0/tls-connection/index.html b/docs/2.15.0/tls-connection/index.html index 47d441d073..09e671e890 100644 --- a/docs/2.15.0/tls-connection/index.html +++ b/docs/2.15.0/tls-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie and OpenEdX over TLS for development | Richie - - + +
    -
    Version: 2.15.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 +

    Version: 2.15.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 and uses a fake LMS backend (base.BaseLMSBackend) as you can see if you check the RICHIE_LMS_BACKENDS setting in env.d/development.

    This base backend uses session storage to fake enrollments to course runs.

    If you want to test real enrollments to an OpenEdX instance hosted on an external domain, OpenEdX will need to generate a CORS CSRF Cookie. This cookie is flagged as secure, which implies that @@ -32,7 +32,7 @@ to share cookies with the OpenEdx application to allow CORS CSRF Protected XHR requests.

    4. Start Richie and OpenEdx over SSL

    Now, the OpenEdx application should respond on https://edx.local.dev:8073, and Richie on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. The next time you want to use SSL, you can run the following command on both the Richie and OpenEdX projects:

    $ make run-ssl

    Of course, you can still run apps without ssl by using:

    $ make run
    - - + + \ No newline at end of file diff --git a/docs/2.15.0/web-analytics/index.html b/docs/2.15.0/web-analytics/index.html index d29483c224..fb076f9853 100644 --- a/docs/2.15.0/web-analytics/index.html +++ b/docs/2.15.0/web-analytics/index.html @@ -4,19 +4,19 @@ Add web analytics to your site | Richie - - + +
    -
    Version: 2.15.0

    Add web analytics to your site

    Richie has native support to Google Analytics and Google Tag Manager Web Analytics solutions. +

    Version: 2.15.0

    Add web analytics to your site

    Richie has native support to Google Analytics and Google Tag Manager Web Analytics solutions. The purpose of this file is to explain how you can enable one of the supported Web Analytics providers and how you can extend Richie with an alternative solution.

    Google Analytics

    Next, it is described how you can configure the Google Analytics on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Analytics tracking id code.

    The current Google Analytics implementation also includes custom dimensions. Those dimensions permit you to create further analyses on Google Analytics or even use them to create custom reports. Custom dimensions with a value as example:

    • Organizations codes - UNIV_LISBON | UNIV_PORTO
    • Course code - COURSE_XPTO
    • Course runs titles - Summer edition | Winter edition
    • Course runs resource links - http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/info
    • Page title - Introduction to Programming

    Google Tag Manager

    Next, it is described how you can configure the Google Tag Manager on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Tag Manager tracking id code.
    • Add the WEB_ANALYTICS_PROVIDER setting with the google_tag_manager value.

    The current Google Tag Manager implementation also defines a custom dimensions like the Google Analytics.

    Location of the web analytics javascript

    Use the WEB_ANALYTICS_LOCATION settings to decide where do you want to put the Javascript code. Use head (default value), to put the Javascript on HTML header, or footer, to put the Javascript code to the bottom of the body.

    Add a new Web Analytics solution

    In this section it's described how you can add support to a different Web Analytics solution.

    • override the richie/web_analytics.html template
    • define the WEB_ANALYTICS_ID setting with your tracking identification
    • define the WEB_ANALYTICS_PROVIDER setting with a value that represents your solution, eg. my-custom-web-analytics-software
    • optionally change WEB_ANALYTICS_LOCATION setting with head (default) or footer value
    • Example of a richie/web_analytics.html file customization that prints to the browser console log the dimension keys and values:
    <script type="text/javascript">
    {% for dimension_key, dimension_value_list in WEB_ANALYTICS.DIMENSIONS.items %}
    console.log("dimension: index '{{forloop.counter}}' with key '{{ dimension_key }}' with value '{{ dimension_value_list|join:" | " }}'");
    {% endfor %}
    </script>

    Output:

    dimension: index '1' with key 'organizations_codes' with value 'COMPATIBLE-EVEN-KEELED-UTILIZATION-19 | FOCUSED-NEXT-GENERATION-FUNCTIONALITIES-22 | UNIVERSAL-MODULAR-LOCAL-AREA-NETWORK-23'
    dimension: index '2' with key 'course_code' with value '00017'
    dimension: index '3' with key 'course_runs_titles' with value 'Run 0'
    dimension: index '4' with key 'course_runs_resource_links' with value ''
    dimension: index '5' with key 'page_title' with value 'Business-focused zero-defect application'

    But you can also contribute to Richie by creating a pull request to add support for a different web analytics solution. In this last case, you have to edit directly the richie/web_analytics.html template.

    Example of an override of the richie/web_analytics.html file:

    {% block web_analytics %}
    {% if WEB_ANALYTICS_ID %}
    {% if WEB_ANALYTICS_PROVIDER == "my_custom_web_analytics_software" %}
    <script type="text/javascript" src="{% static 'myapp/js/custom_web_analytics_software.js' %}">
    <script type="text/javascript">
    // javascript code that startups the custom web analytics software
    </script>
    {% endif %}
    {% endif %}
    {% endblock web_analytics %}

    The web analytics dimensions are being added to the django context using the WEB_ANALYTICS.DIMENSIONS dictionary. Because each dimension value could have multiple values, then each dictionary value is a list. Web analytics dimensions dictionary keys:

    • organizations_codes
    • course_code
    • course_runs_titles
    • course_runs_resource_links
    • page_title

    Example, if you only need the organization codes on your custom richie/web_analytics.html file:

    <script type="text/javascript">
    console.log("organization codes: '{{ WEB_ANALYTICS.DIMENSIONS.organizations_codes |join:" | " }}");
    </script>

    The frontend code also sends events to the web analytics provider. Richie sends events when the user is enrolled on a course run. To support different providers, you need to create a similar file of src/frontend/js/utils/api/web-analytics/google_analytics.ts and change the src/frontend/js/utils/api/web-analytics/index.ts file to include that newer provider.

    - - + + \ No newline at end of file diff --git a/docs/2.15.1/accessibility-testing/index.html b/docs/2.15.1/accessibility-testing/index.html index 410f0741d8..6fe614102e 100644 --- a/docs/2.15.1/accessibility-testing/index.html +++ b/docs/2.15.1/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.15.1

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.15.1

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.15.1/api/course-run-synchronization-api/index.html b/docs/2.15.1/api/course-run-synchronization-api/index.html index cda2e2ec36..5f5ea2ff74 100644 --- a/docs/2.15.1/api/course-run-synchronization-api/index.html +++ b/docs/2.15.1/api/course-run-synchronization-api/index.html @@ -4,12 +4,12 @@ Course run synchronization API | Richie - - + +
    -
    Version: 2.15.1

    Course run synchronization API

    API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

    Synchronization endpoint [/api/1.0/course-runs-sync]

    This documentation describes version "1.0" of this API endpoint.

    Synchronize a course run [POST]

    It takes a JSON object containing the course run details:

    • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - +
      Version: 2.15.1

      Course run synchronization API

      API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

      Synchronization endpoint [/api/1.0/course-runs-sync]

      This documentation describes version "1.0" of this API endpoint.

      Synchronize a course run [POST]

      It takes a JSON object containing the course run details:

      • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - url of the course syllabus on the LMS from which a unique course identifier can be extracted
      • start: 2018-02-01T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course starts
      • end: 2018-02-28T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course ends
      • enrollment_start: 2018-01-01T06:00:00Z (string, optional) - ISO 8601 date, when enrollment @@ -19,7 +19,7 @@ containing the digest of the utf-8 encoded json representation of the submitted data for the given secret key and SHA256 digest algorithm (see [synchronizing-course-runs] for an example).
    • Body

        {
      "resource_link": "https://lms.example.com/courses/course-v1:001+001+001/info",
      "start": "2021-02-01T00:00:00Z",
      "end": "2021-02-31T23:59:59Z",
      "enrollment_start": "2021-01-01T00:00:00Z",
      "enrollment_end": "2021-01-31T23:59:59Z",
      "languages": ["en", "fr"]
      }
  • Response 200 (application/json)

    • Body

        {
      "success": True
      }
  • - - + + \ No newline at end of file diff --git a/docs/2.15.1/building-the-frontend/index.html b/docs/2.15.1/building-the-frontend/index.html index 40091c0eed..cd505df4a0 100644 --- a/docs/2.15.1/building-the-frontend/index.html +++ b/docs/2.15.1/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.15.1

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.15.1

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.15.1/contributing-guide/index.html b/docs/2.15.1/contributing-guide/index.html index 0ca08b748d..b0aa4aa225 100644 --- a/docs/2.15.1/contributing-guide/index.html +++ b/docs/2.15.1/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.15.1

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.15.1

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.15.1/cookiecutter/index.html b/docs/2.15.1/cookiecutter/index.html index 2b4ebaa041..01c233f6d3 100644 --- a/docs/2.15.1/cookiecutter/index.html +++ b/docs/2.15.1/cookiecutter/index.html @@ -4,12 +4,12 @@ Start your own site | Richie - - + +
    -
    Version: 2.15.1

    Start your own site

    We use Cookiecutter to help you +

    Version: 2.15.1

    Start your own site

    We use Cookiecutter to help you set up a production-ready learning portal website based on Richie in seconds.

    Run Cookiecutter

    There are 2 options to run Cookiecutter:

    While you think of it, navigate to the directory in which you want to create your site factory:

    cd /path/to/your/code/directory

    If you chose to install Cookiecutter, you can now run it against our @@ -30,7 +30,7 @@ project's scaffolding but, don't worry, it will respect all the sites you will have created in the sites directory:

    cookiecutter --overwrite-if-exists gh:openfun/richie --directory=cookiecutter

    Help us improve this project

    After starting your project, please submit an issue let us know how it went and what other features we should add to make it better.

    - - + + \ No newline at end of file diff --git a/docs/2.15.1/css-guidelines/index.html b/docs/2.15.1/css-guidelines/index.html index 5cc227d002..471e934c81 100644 --- a/docs/2.15.1/css-guidelines/index.html +++ b/docs/2.15.1/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.15.1

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.15.1

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.15.1/discover/index.html b/docs/2.15.1/discover/index.html index e0fd3d1830..44c345eb8a 100644 --- a/docs/2.15.1/discover/index.html +++ b/docs/2.15.1/discover/index.html @@ -4,12 +4,12 @@ Discover Richie | Richie - - + +
    -
    Version: 2.15.1

    Discover Richie

    Learning Management Systems (LMS) are great tools for hosting and playing interactive online +

    Version: 2.15.1

    Discover Richie

    Learning Management Systems (LMS) are great tools for hosting and playing interactive online courses and MOOCs.

    However, if you need to build a complete website with flexible content to aggregate your courses, in several languages and from different sources, you will soon need a CMS.

    At "France Université Numérique", we wanted to build an OpenSource portal with Python and Django. That's why we built Richie on top of DjangoCMS, one of @@ -21,7 +21,7 @@ OpenEdX Docker.

    Two users are available for testing:

    • admin: admin@example.com/admin
    • student: edx@example.com/edx

    The database is regularly flushed.

    Start your own site

    The next step after discovering Richie on the demo is to start your own project. We provide a production-ready template based on Cookiecutter that allows you to generate your project in seconds.

    Follow the guide on starting your own Richie site with Cookiecutter.

    Once you created a new site, you will be able to fully customize it:

    - - + + \ No newline at end of file diff --git a/docs/2.15.1/displaying-connection-status/index.html b/docs/2.15.1/displaying-connection-status/index.html index d5fdeb9382..da7aa7337e 100644 --- a/docs/2.15.1/displaying-connection-status/index.html +++ b/docs/2.15.1/displaying-connection-status/index.html @@ -4,12 +4,12 @@ Displaying OpenEdX connection status in Richie | Richie - - + +
    -
    Version: 2.15.1

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status +

    Version: 2.15.1

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status and display profile information for the logged-in user in Richie.

    In Richie, the only users that are actually authenticated on the DjangoCMS instance producing the site, are editors and staff users. Your instructors and learners will not have user accounts on Richie, but authentication is delegated to a remote service that can be OpenEdX, KeyCloack, or @@ -26,7 +26,7 @@ set for the above authentication delegation BASE_URL setting.

    If you need to bind user data into a url, wrap the property between brackets. For example, the link configured above for the profile page {base_url:s}/u/(username) would point to https://lms.example.com/u/johndoe for a user carrying the username johndoe.

    - - + + \ No newline at end of file diff --git a/docs/2.15.1/django-react-interop/index.html b/docs/2.15.1/django-react-interop/index.html index f57b0823ea..12f3c2679a 100644 --- a/docs/2.15.1/django-react-interop/index.html +++ b/docs/2.15.1/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.15.1

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.15.1

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.15.1/docker-development/index.html b/docs/2.15.1/docker-development/index.html index 66bcb1f695..22ec69ba6a 100644 --- a/docs/2.15.1/docker-development/index.html +++ b/docs/2.15.1/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.15.1

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.15.1

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.15.1/frontend-overrides/index.html b/docs/2.15.1/frontend-overrides/index.html index 5d23f1c6ae..e096de9fbb 100644 --- a/docs/2.15.1/frontend-overrides/index.html +++ b/docs/2.15.1/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.15.1

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.15.1

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.15.1/installation/index.html b/docs/2.15.1/installation/index.html index aa1aff241f..efffa44c78 100644 --- a/docs/2.15.1/installation/index.html +++ b/docs/2.15.1/installation/index.html @@ -4,12 +4,12 @@ Installing Richie for development | Richie - - + +
    -
    Version: 2.15.1

    Installing Richie for development

    Richie is a container-native application but can also be installed +

    Version: 2.15.1

    Installing Richie for development

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of:

    • 3 running services:

      • database: postgresql or mysql at your preference,
      • elasticsearch: the search engine,
      • app: the actual DjangoCMS project with all our application code.
    • 2 containers for building purposes:

    - - + + \ No newline at end of file diff --git a/docs/2.15.1/internationalization/index.html b/docs/2.15.1/internationalization/index.html index 73ea0009ce..914c41d251 100644 --- a/docs/2.15.1/internationalization/index.html +++ b/docs/2.15.1/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.15.1

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.15.1

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.15.1/joanie-connection/index.html b/docs/2.15.1/joanie-connection/index.html index bd6eaffac9..bc2189d069 100644 --- a/docs/2.15.1/joanie-connection/index.html +++ b/docs/2.15.1/joanie-connection/index.html @@ -4,12 +4,12 @@ Joanie Connection | Richie - - + +
    -
    Version: 2.15.1

    Joanie Connection

    Settings

    All settings related to Joanie have to be declared in the JOANIE dictionary +

    Version: 2.15.1

    Joanie Connection

    Settings

    All settings related to Joanie have to be declared in the JOANIE dictionary within settings.py. To enable Joanie, the minimal configuration requires one property:

    • BASE_URL : the endpoint at which Joanie is accessible

    Add to your settings.py:

    ...
    JOANIE = {
    "BASE_URL": values.Value(environ_name="JOANIE_BASE_URL", environ_prefix=None)
    }
    ...

    Access Token

    Lifetime configuration

    Access Token is stored within the SessionStorage through react-query client persistor. @@ -19,7 +19,7 @@ lifetime of the access token defined by your authentication server. For example, if your authentication server is using djangorestframework-simplejwt to generate the access token, its lifetime is 5 minutes by default.

    - - + + \ No newline at end of file diff --git a/docs/2.15.1/lms-backends/index.html b/docs/2.15.1/lms-backends/index.html index 159af7cb69..e431f6b223 100644 --- a/docs/2.15.1/lms-backends/index.html +++ b/docs/2.15.1/lms-backends/index.html @@ -4,12 +4,12 @@ Configuring LMS Backends | Richie - - + +
    -
    Version: 2.15.1

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless +

    Version: 2.15.1

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless experience between browsing the course catalog on Richie, enrolling to a course and following the course itself on the LMS.

    It is possible to do the same with Moodle or any other LMS exposing an enrollment API, at the cost of writing a custom LMS handler backend.

    Prerequisites

    This connection requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that @@ -36,7 +36,7 @@ value other then manual.

    • Type: enum(string)
    • Required: No
    • Value: for example ["languages"]

    Technical support

    If you encounter an issue with this documentation or the backends included in Richie, please open an issue on our repository.

    If you need a custom backend, you can submit a PR or open an issue and we will consider adding it.

    - - + + \ No newline at end of file diff --git a/docs/2.15.1/lms-connection/index.html b/docs/2.15.1/lms-connection/index.html index a3856d214d..d8a43a6e2d 100644 --- a/docs/2.15.1/lms-connection/index.html +++ b/docs/2.15.1/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with one or more LMS | Richie - - + +
    -
    Version: 2.15.1

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated +

    Version: 2.15.1

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated seamless experience.

    As of today, each approach has been implemented for OpenEdX but the same could be done for other LMSes like Moodle, at the cost of minor adaptations.

    1. Displaying connection status

    OpenEdX can be configured to allow CORS requests. Doing so allows Richie to retrieve a user's connection status from OpenEdx and display the user's profile information directly on the Richie @@ -31,7 +31,7 @@ docker-compose.

    If you want to activate seamless enrollment locally for development, you will need to set up TLS domains for both Richie and OpenEdX. To do this, head over to our guide on setting-up TLS connections for Richie and OpenEdX.

    - - + + \ No newline at end of file diff --git a/docs/2.15.1/native-installation/index.html b/docs/2.15.1/native-installation/index.html index 73e9b7b9a1..c2be558b48 100644 --- a/docs/2.15.1/native-installation/index.html +++ b/docs/2.15.1/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.15.1

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.15.1

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native installation instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.15.1/synchronizing-course-runs/index.html b/docs/2.15.1/synchronizing-course-runs/index.html index 30eb8d761a..98405c495d 100644 --- a/docs/2.15.1/synchronizing-course-runs/index.html +++ b/docs/2.15.1/synchronizing-course-runs/index.html @@ -4,12 +4,12 @@ Synchronizing course runs between Richie and OpenEdX | Richie - - + +
    -
    Version: 2.15.1

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the +

    Version: 2.15.1

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the RICHIE_COURSE_RUN_SYNC_SECRETS setting with one or more secrets:

    RICHIE_COURSE_RUN_SYNC_SECRETS = ["SharedSecret", "OtherSharedSecret"]

    This setting collects several secrets in order to allow rotating them without any downtime. Any of the secrets listed in this setting can be used to sign your queries.

    Your secret should be shared with the LMS or distant system that needs to synchronize its course runs with the Richie instance. Richie will try the declared secrets one by one until it finds @@ -29,7 +29,7 @@ course run is modified and published.

    Or you can use the Richie Open edX Synchronization which is based on the following code sample and also includes the enrollment count.

    Given a COURSE_HOOK setting defined as follows in your OpenEdX instance:

    COURSE_HOOK = {
    "secret": "SharedSecret",
    "url": "https://richie.example.com/api/v1.0/course-runs-sync/",
    }

    The code for the synchronization function in OpenEdX could look like this:

    import hashlib
    import hmac
    import json

    from django.conf import settings

    from microsite_configuration import microsite
    import requests
    from xmodule.modulestore.django import modulestore


    def update_course(course_key, *args, **kwargs):
    """Synchronize an OpenEdX course, identified by its course key, with a Richie instance."""
    course = modulestore().get_course(course_key)
    edxapp_domain = microsite.get_value("site_domain", settings.LMS_BASE)

    data = {
    "resource_link": "https://{:s}/courses/{!s}/info".format(
    edxapp_domain, course_key
    ),
    "start": course.start and course.start.isoformat(),
    "end": course.end and course.end.isoformat(),
    "enrollment_start": course.enrollment_start and course.enrollment_start.isoformat(),
    "enrollment_end": course.enrollment_end and course.enrollment_end.isoformat(),
    "languages": [course.language or settings.LANGUAGE_CODE],
    }

    signature = hmac.new(
    setting.COURSE_HOOK["secret"].encode("utf-8"),
    msg=json.dumps(data).encode("utf-8"),
    digestmod=hashlib.sha256,
    ).hexdigest()

    response = requests.post(
    setting.COURSE_HOOK["url"],
    json=data,
    headers={"Authorization": "SIG-HMAC-SHA256 {:s}".format(signature)},
    )

    Thanks to the signal emitted in OpenEdX, this function can then be triggered each time a course is modified and published:

    from django.dispatch import receiver
    from xmodule.modulestore.django import SignalHandler


    @receiver(SignalHandler.course_published, dispatch_uid='update_course_on_publish')
    def update_course_on_publish(sender, course_key, **kwargs):
    update_course(course_key)
    - - + + \ No newline at end of file diff --git a/docs/2.15.1/tls-connection/index.html b/docs/2.15.1/tls-connection/index.html index 99d0113be5..4daf6670d0 100644 --- a/docs/2.15.1/tls-connection/index.html +++ b/docs/2.15.1/tls-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie and OpenEdX over TLS for development | Richie - - + +
    -
    Version: 2.15.1

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 +

    Version: 2.15.1

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 and uses a fake LMS backend (base.BaseLMSBackend) as you can see if you check the RICHIE_LMS_BACKENDS setting in env.d/development.

    This base backend uses session storage to fake enrollments to course runs.

    If you want to test real enrollments to an OpenEdX instance hosted on an external domain, OpenEdX will need to generate a CORS CSRF Cookie. This cookie is flagged as secure, which implies that @@ -32,7 +32,7 @@ to share cookies with the OpenEdx application to allow CORS CSRF Protected XHR requests.

    4. Start Richie and OpenEdx over SSL

    Now, the OpenEdx application should respond on https://edx.local.dev:8073, and Richie on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. The next time you want to use SSL, you can run the following command on both the Richie and OpenEdX projects:

    $ make run-ssl

    Of course, you can still run apps without ssl by using:

    $ make run
    - - + + \ No newline at end of file diff --git a/docs/2.15.1/web-analytics/index.html b/docs/2.15.1/web-analytics/index.html index c1ab1688fa..d004f74826 100644 --- a/docs/2.15.1/web-analytics/index.html +++ b/docs/2.15.1/web-analytics/index.html @@ -4,19 +4,19 @@ Add web analytics to your site | Richie - - + +
    -
    Version: 2.15.1

    Add web analytics to your site

    Richie has native support to Google Analytics and Google Tag Manager Web Analytics solutions. +

    Version: 2.15.1

    Add web analytics to your site

    Richie has native support to Google Analytics and Google Tag Manager Web Analytics solutions. The purpose of this file is to explain how you can enable one of the supported Web Analytics providers and how you can extend Richie with an alternative solution.

    Google Analytics

    Next, it is described how you can configure the Google Analytics on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Analytics tracking id code.

    The current Google Analytics implementation also includes custom dimensions. Those dimensions permit you to create further analyses on Google Analytics or even use them to create custom reports. Custom dimensions with a value as example:

    • Organizations codes - UNIV_LISBON | UNIV_PORTO
    • Course code - COURSE_XPTO
    • Course runs titles - Summer edition | Winter edition
    • Course runs resource links - http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/info
    • Page title - Introduction to Programming

    Google Tag Manager

    Next, it is described how you can configure the Google Tag Manager on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Tag Manager tracking id code.
    • Add the WEB_ANALYTICS_PROVIDER setting with the google_tag_manager value.

    The current Google Tag Manager implementation also defines a custom dimensions like the Google Analytics.

    Location of the web analytics javascript

    Use the WEB_ANALYTICS_LOCATION settings to decide where do you want to put the Javascript code. Use head (default value), to put the Javascript on HTML header, or footer, to put the Javascript code to the bottom of the body.

    Add a new Web Analytics solution

    In this section it's described how you can add support to a different Web Analytics solution.

    • override the richie/web_analytics.html template
    • define the WEB_ANALYTICS_ID setting with your tracking identification
    • define the WEB_ANALYTICS_PROVIDER setting with a value that represents your solution, eg. my-custom-web-analytics-software
    • optionally change WEB_ANALYTICS_LOCATION setting with head (default) or footer value
    • Example of a richie/web_analytics.html file customization that prints to the browser console log the dimension keys and values:
    <script type="text/javascript">
    {% for dimension_key, dimension_value_list in WEB_ANALYTICS.DIMENSIONS.items %}
    console.log("dimension: index '{{forloop.counter}}' with key '{{ dimension_key }}' with value '{{ dimension_value_list|join:" | " }}'");
    {% endfor %}
    </script>

    Output:

    dimension: index '1' with key 'organizations_codes' with value 'COMPATIBLE-EVEN-KEELED-UTILIZATION-19 | FOCUSED-NEXT-GENERATION-FUNCTIONALITIES-22 | UNIVERSAL-MODULAR-LOCAL-AREA-NETWORK-23'
    dimension: index '2' with key 'course_code' with value '00017'
    dimension: index '3' with key 'course_runs_titles' with value 'Run 0'
    dimension: index '4' with key 'course_runs_resource_links' with value ''
    dimension: index '5' with key 'page_title' with value 'Business-focused zero-defect application'

    But you can also contribute to Richie by creating a pull request to add support for a different web analytics solution. In this last case, you have to edit directly the richie/web_analytics.html template.

    Example of an override of the richie/web_analytics.html file:

    {% block web_analytics %}
    {% if WEB_ANALYTICS_ID %}
    {% if WEB_ANALYTICS_PROVIDER == "my_custom_web_analytics_software" %}
    <script type="text/javascript" src="{% static 'myapp/js/custom_web_analytics_software.js' %}">
    <script type="text/javascript">
    // javascript code that startups the custom web analytics software
    </script>
    {% endif %}
    {% endif %}
    {% endblock web_analytics %}

    The web analytics dimensions are being added to the django context using the WEB_ANALYTICS.DIMENSIONS dictionary. Because each dimension value could have multiple values, then each dictionary value is a list. Web analytics dimensions dictionary keys:

    • organizations_codes
    • course_code
    • course_runs_titles
    • course_runs_resource_links
    • page_title

    Example, if you only need the organization codes on your custom richie/web_analytics.html file:

    <script type="text/javascript">
    console.log("organization codes: '{{ WEB_ANALYTICS.DIMENSIONS.organizations_codes |join:" | " }}");
    </script>

    The frontend code also sends events to the web analytics provider. Richie sends events when the user is enrolled on a course run. To support different providers, you need to create a similar file of src/frontend/js/utils/api/web-analytics/google_analytics.ts and change the src/frontend/js/utils/api/web-analytics/index.ts file to include that newer provider.

    - - + + \ No newline at end of file diff --git a/docs/2.16.0/accessibility-testing/index.html b/docs/2.16.0/accessibility-testing/index.html index a11a4482c9..a3b3658695 100644 --- a/docs/2.16.0/accessibility-testing/index.html +++ b/docs/2.16.0/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.16.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.16.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.16.0/api/course-run-synchronization-api/index.html b/docs/2.16.0/api/course-run-synchronization-api/index.html index c10f788cfb..f1fa5778fd 100644 --- a/docs/2.16.0/api/course-run-synchronization-api/index.html +++ b/docs/2.16.0/api/course-run-synchronization-api/index.html @@ -4,12 +4,12 @@ Course run synchronization API | Richie - - + +
    -
    Version: 2.16.0

    Course run synchronization API

    API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

    Synchronization endpoint [/api/1.0/course-runs-sync]

    This documentation describes version "1.0" of this API endpoint.

    Synchronize a course run [POST]

    It takes a JSON object containing the course run details:

    • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - +
      Version: 2.16.0

      Course run synchronization API

      API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

      Synchronization endpoint [/api/1.0/course-runs-sync]

      This documentation describes version "1.0" of this API endpoint.

      Synchronize a course run [POST]

      It takes a JSON object containing the course run details:

      • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - url of the course syllabus on the LMS from which a unique course identifier can be extracted
      • start: 2018-02-01T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course starts
      • end: 2018-02-28T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course ends
      • enrollment_start: 2018-01-01T06:00:00Z (string, optional) - ISO 8601 date, when enrollment @@ -19,7 +19,7 @@ containing the digest of the utf-8 encoded json representation of the submitted data for the given secret key and SHA256 digest algorithm (see [synchronizing-course-runs] for an example).
    • Body

        {
      "resource_link": "https://lms.example.com/courses/course-v1:001+001+001/info",
      "start": "2021-02-01T00:00:00Z",
      "end": "2021-02-31T23:59:59Z",
      "enrollment_start": "2021-01-01T00:00:00Z",
      "enrollment_end": "2021-01-31T23:59:59Z",
      "languages": ["en", "fr"]
      }
  • Response 200 (application/json)

    • Body

        {
      "success": True
      }
  • - - + + \ No newline at end of file diff --git a/docs/2.16.0/building-the-frontend/index.html b/docs/2.16.0/building-the-frontend/index.html index 11eec4f8ea..76c23255fc 100644 --- a/docs/2.16.0/building-the-frontend/index.html +++ b/docs/2.16.0/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.16.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.16.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.16.0/contributing-guide/index.html b/docs/2.16.0/contributing-guide/index.html index 529d22df6d..dfe96f340c 100644 --- a/docs/2.16.0/contributing-guide/index.html +++ b/docs/2.16.0/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.16.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.16.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.16.0/cookiecutter/index.html b/docs/2.16.0/cookiecutter/index.html index 24e9eee0ca..341f7f9f3f 100644 --- a/docs/2.16.0/cookiecutter/index.html +++ b/docs/2.16.0/cookiecutter/index.html @@ -4,12 +4,12 @@ Start your own site | Richie - - + +
    -
    Version: 2.16.0

    Start your own site

    We use Cookiecutter to help you +

    Version: 2.16.0

    Start your own site

    We use Cookiecutter to help you set up a production-ready learning portal website based on Richie in seconds.

    Run Cookiecutter

    There are 2 options to run Cookiecutter:

    While you think of it, navigate to the directory in which you want to create your site factory:

    cd /path/to/your/code/directory

    If you chose to install Cookiecutter, you can now run it against our @@ -30,7 +30,7 @@ project's scaffolding but, don't worry, it will respect all the sites you will have created in the sites directory:

    cookiecutter --overwrite-if-exists gh:openfun/richie --directory=cookiecutter

    Help us improve this project

    After starting your project, please submit an issue let us know how it went and what other features we should add to make it better.

    - - + + \ No newline at end of file diff --git a/docs/2.16.0/css-guidelines/index.html b/docs/2.16.0/css-guidelines/index.html index 6d1f382ecd..b60d166290 100644 --- a/docs/2.16.0/css-guidelines/index.html +++ b/docs/2.16.0/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.16.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.16.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.16.0/discover/index.html b/docs/2.16.0/discover/index.html index ea65b283de..a4b2847e44 100644 --- a/docs/2.16.0/discover/index.html +++ b/docs/2.16.0/discover/index.html @@ -4,12 +4,12 @@ Discover Richie | Richie - - + +
    -
    Version: 2.16.0

    Discover Richie

    Learning Management Systems (LMS) are great tools for hosting and playing interactive online +

    Version: 2.16.0

    Discover Richie

    Learning Management Systems (LMS) are great tools for hosting and playing interactive online courses and MOOCs.

    However, if you need to build a complete website with flexible content to aggregate your courses, in several languages and from different sources, you will soon need a CMS.

    At "France Université Numérique", we wanted to build an OpenSource portal with Python and Django. That's why we built Richie on top of DjangoCMS, one of @@ -21,7 +21,7 @@ OpenEdX Docker.

    Two users are available for testing:

    • admin: admin@example.com/admin
    • student: edx@example.com/edx

    The database is regularly flushed.

    Start your own site

    The next step after discovering Richie on the demo is to start your own project. We provide a production-ready template based on Cookiecutter that allows you to generate your project in seconds.

    Follow the guide on starting your own Richie site with Cookiecutter.

    Once you created a new site, you will be able to fully customize it:

    - - + + \ No newline at end of file diff --git a/docs/2.16.0/displaying-connection-status/index.html b/docs/2.16.0/displaying-connection-status/index.html index 902c869945..5fb6b7e96f 100644 --- a/docs/2.16.0/displaying-connection-status/index.html +++ b/docs/2.16.0/displaying-connection-status/index.html @@ -4,12 +4,12 @@ Displaying OpenEdX connection status in Richie | Richie - - + +
    -
    Version: 2.16.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status +

    Version: 2.16.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status and display profile information for the logged-in user in Richie.

    In Richie, the only users that are actually authenticated on the DjangoCMS instance producing the site, are editors and staff users. Your instructors and learners will not have user accounts on Richie, but authentication is delegated to a remote service that can be OpenEdX, KeyCloack, or @@ -26,7 +26,7 @@ set for the above authentication delegation BASE_URL setting.

    If you need to bind user data into a url, wrap the property between brackets. For example, the link configured above for the profile page {base_url:s}/u/(username) would point to https://lms.example.com/u/johndoe for a user carrying the username johndoe.

    - - + + \ No newline at end of file diff --git a/docs/2.16.0/django-react-interop/index.html b/docs/2.16.0/django-react-interop/index.html index 9bddc2ef7d..abd4055ece 100644 --- a/docs/2.16.0/django-react-interop/index.html +++ b/docs/2.16.0/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.16.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.16.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.16.0/docker-development/index.html b/docs/2.16.0/docker-development/index.html index 91508b9390..eff000cd21 100644 --- a/docs/2.16.0/docker-development/index.html +++ b/docs/2.16.0/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.16.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.16.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.16.0/frontend-overrides/index.html b/docs/2.16.0/frontend-overrides/index.html index 2cf6c96112..f92d45d69a 100644 --- a/docs/2.16.0/frontend-overrides/index.html +++ b/docs/2.16.0/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.16.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.16.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.16.0/installation/index.html b/docs/2.16.0/installation/index.html index 3e85e2d256..1a8593642d 100644 --- a/docs/2.16.0/installation/index.html +++ b/docs/2.16.0/installation/index.html @@ -4,12 +4,12 @@ Installing Richie for development | Richie - - + +
    -
    Version: 2.16.0

    Installing Richie for development

    Richie is a container-native application but can also be installed +

    Version: 2.16.0

    Installing Richie for development

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of:

    • 3 running services:

      • database: postgresql or mysql at your preference,
      • elasticsearch: the search engine,
      • app: the actual DjangoCMS project with all our application code.
    • 2 containers for building purposes:

    - - + + \ No newline at end of file diff --git a/docs/2.16.0/internationalization/index.html b/docs/2.16.0/internationalization/index.html index 5d836316f6..ff164ec76f 100644 --- a/docs/2.16.0/internationalization/index.html +++ b/docs/2.16.0/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.16.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.16.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.16.0/joanie-connection/index.html b/docs/2.16.0/joanie-connection/index.html index a8601c1308..8023acd6ed 100644 --- a/docs/2.16.0/joanie-connection/index.html +++ b/docs/2.16.0/joanie-connection/index.html @@ -4,12 +4,12 @@ Joanie Connection | Richie - - + +
    -
    Version: 2.16.0

    Joanie Connection

    Settings

    All settings related to Joanie have to be declared in the JOANIE dictionary +

    Version: 2.16.0

    Joanie Connection

    Settings

    All settings related to Joanie have to be declared in the JOANIE dictionary within settings.py. To enable Joanie, the minimal configuration requires one property:

    • BASE_URL : the endpoint at which Joanie is accessible

    Add to your settings.py:

    ...
    JOANIE = {
    "BASE_URL": values.Value(environ_name="JOANIE_BASE_URL", environ_prefix=None)
    }
    ...

    Access Token

    Lifetime configuration

    Access Token is stored within the SessionStorage through react-query client persistor. @@ -19,7 +19,7 @@ lifetime of the access token defined by your authentication server. For example, if your authentication server is using djangorestframework-simplejwt to generate the access token, its lifetime is 5 minutes by default.

    - - + + \ No newline at end of file diff --git a/docs/2.16.0/lms-backends/index.html b/docs/2.16.0/lms-backends/index.html index 081e1120b6..db2172ee53 100644 --- a/docs/2.16.0/lms-backends/index.html +++ b/docs/2.16.0/lms-backends/index.html @@ -4,12 +4,12 @@ Configuring LMS Backends | Richie - - + +
    -
    Version: 2.16.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless +

    Version: 2.16.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless experience between browsing the course catalog on Richie, enrolling to a course and following the course itself on the LMS.

    It is possible to do the same with Moodle or any other LMS exposing an enrollment API, at the cost of writing a custom LMS handler backend.

    Prerequisites

    This connection requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that @@ -36,7 +36,7 @@ value other then manual.

    • Type: enum(string)
    • Required: No
    • Value: for example ["languages"]

    Technical support

    If you encounter an issue with this documentation or the backends included in Richie, please open an issue on our repository.

    If you need a custom backend, you can submit a PR or open an issue and we will consider adding it.

    - - + + \ No newline at end of file diff --git a/docs/2.16.0/lms-connection/index.html b/docs/2.16.0/lms-connection/index.html index 8c79cc4b16..aa3b6d2401 100644 --- a/docs/2.16.0/lms-connection/index.html +++ b/docs/2.16.0/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with one or more LMS | Richie - - + +
    -
    Version: 2.16.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated +

    Version: 2.16.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated seamless experience.

    As of today, each approach has been implemented for OpenEdX but the same could be done for other LMSes like Moodle, at the cost of minor adaptations.

    1. Displaying connection status

    OpenEdX can be configured to allow CORS requests. Doing so allows Richie to retrieve a user's connection status from OpenEdx and display the user's profile information directly on the Richie @@ -31,7 +31,7 @@ docker-compose.

    If you want to activate seamless enrollment locally for development, you will need to set up TLS domains for both Richie and OpenEdX. To do this, head over to our guide on setting-up TLS connections for Richie and OpenEdX.

    - - + + \ No newline at end of file diff --git a/docs/2.16.0/native-installation/index.html b/docs/2.16.0/native-installation/index.html index 3f25b0a834..b9117c12ba 100644 --- a/docs/2.16.0/native-installation/index.html +++ b/docs/2.16.0/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.16.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.16.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native installation instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.16.0/synchronizing-course-runs/index.html b/docs/2.16.0/synchronizing-course-runs/index.html index 7054df70cc..016df66f82 100644 --- a/docs/2.16.0/synchronizing-course-runs/index.html +++ b/docs/2.16.0/synchronizing-course-runs/index.html @@ -4,12 +4,12 @@ Synchronizing course runs between Richie and OpenEdX | Richie - - + +
    -
    Version: 2.16.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the +

    Version: 2.16.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the RICHIE_COURSE_RUN_SYNC_SECRETS setting with one or more secrets:

    RICHIE_COURSE_RUN_SYNC_SECRETS = ["SharedSecret", "OtherSharedSecret"]

    This setting collects several secrets in order to allow rotating them without any downtime. Any of the secrets listed in this setting can be used to sign your queries.

    Your secret should be shared with the LMS or distant system that needs to synchronize its course runs with the Richie instance. Richie will try the declared secrets one by one until it finds @@ -29,7 +29,7 @@ course run is modified and published.

    Or you can use the Richie Open edX Synchronization which is based on the following code sample and also includes the enrollment count.

    Given a COURSE_HOOK setting defined as follows in your OpenEdX instance:

    COURSE_HOOK = {
    "secret": "SharedSecret",
    "url": "https://richie.example.com/api/v1.0/course-runs-sync/",
    }

    The code for the synchronization function in OpenEdX could look like this:

    import hashlib
    import hmac
    import json

    from django.conf import settings

    from microsite_configuration import microsite
    import requests
    from xmodule.modulestore.django import modulestore


    def update_course(course_key, *args, **kwargs):
    """Synchronize an OpenEdX course, identified by its course key, with a Richie instance."""
    course = modulestore().get_course(course_key)
    edxapp_domain = microsite.get_value("site_domain", settings.LMS_BASE)

    data = {
    "resource_link": "https://{:s}/courses/{!s}/info".format(
    edxapp_domain, course_key
    ),
    "start": course.start and course.start.isoformat(),
    "end": course.end and course.end.isoformat(),
    "enrollment_start": course.enrollment_start and course.enrollment_start.isoformat(),
    "enrollment_end": course.enrollment_end and course.enrollment_end.isoformat(),
    "languages": [course.language or settings.LANGUAGE_CODE],
    }

    signature = hmac.new(
    setting.COURSE_HOOK["secret"].encode("utf-8"),
    msg=json.dumps(data).encode("utf-8"),
    digestmod=hashlib.sha256,
    ).hexdigest()

    response = requests.post(
    setting.COURSE_HOOK["url"],
    json=data,
    headers={"Authorization": "SIG-HMAC-SHA256 {:s}".format(signature)},
    )

    Thanks to the signal emitted in OpenEdX, this function can then be triggered each time a course is modified and published:

    from django.dispatch import receiver
    from xmodule.modulestore.django import SignalHandler


    @receiver(SignalHandler.course_published, dispatch_uid='update_course_on_publish')
    def update_course_on_publish(sender, course_key, **kwargs):
    update_course(course_key)
    - - + + \ No newline at end of file diff --git a/docs/2.16.0/tls-connection/index.html b/docs/2.16.0/tls-connection/index.html index d868240dd6..9cb286f9b7 100644 --- a/docs/2.16.0/tls-connection/index.html +++ b/docs/2.16.0/tls-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie and OpenEdX over TLS for development | Richie - - + +
    -
    Version: 2.16.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 +

    Version: 2.16.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 and uses a fake LMS backend (base.BaseLMSBackend) as you can see if you check the RICHIE_LMS_BACKENDS setting in env.d/development.

    This base backend uses session storage to fake enrollments to course runs.

    If you want to test real enrollments to an OpenEdX instance hosted on an external domain, OpenEdX will need to generate a CORS CSRF Cookie. This cookie is flagged as secure, which implies that @@ -32,7 +32,7 @@ to share cookies with the OpenEdx application to allow CORS CSRF Protected XHR requests.

    4. Start Richie and OpenEdx over SSL

    Now, the OpenEdx application should respond on https://edx.local.dev:8073, and Richie on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. The next time you want to use SSL, you can run the following command on both the Richie and OpenEdX projects:

    $ make run-ssl

    Of course, you can still run apps without ssl by using:

    $ make run
    - - + + \ No newline at end of file diff --git a/docs/2.16.0/web-analytics/index.html b/docs/2.16.0/web-analytics/index.html index f462cced38..c27bdf62c9 100644 --- a/docs/2.16.0/web-analytics/index.html +++ b/docs/2.16.0/web-analytics/index.html @@ -4,19 +4,19 @@ Add web analytics to your site | Richie - - + +
    -
    Version: 2.16.0

    Add web analytics to your site

    Richie has native support to Google Analytics and Google Tag Manager Web Analytics solutions. +

    Version: 2.16.0

    Add web analytics to your site

    Richie has native support to Google Analytics and Google Tag Manager Web Analytics solutions. The purpose of this file is to explain how you can enable one of the supported Web Analytics providers and how you can extend Richie with an alternative solution.

    Google Analytics

    Next, it is described how you can configure the Google Analytics on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Analytics tracking id code.

    The current Google Analytics implementation also includes custom dimensions. Those dimensions permit you to create further analyses on Google Analytics or even use them to create custom reports. Custom dimensions with a value as example:

    • Organizations codes - UNIV_LISBON | UNIV_PORTO
    • Course code - COURSE_XPTO
    • Course runs titles - Summer edition | Winter edition
    • Course runs resource links - http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/info
    • Page title - Introduction to Programming

    Google Tag Manager

    Next, it is described how you can configure the Google Tag Manager on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Tag Manager tracking id code.
    • Add the WEB_ANALYTICS_PROVIDER setting with the google_tag_manager value.

    The current Google Tag Manager implementation also defines a custom dimensions like the Google Analytics.

    Location of the web analytics javascript

    Use the WEB_ANALYTICS_LOCATION settings to decide where do you want to put the Javascript code. Use head (default value), to put the Javascript on HTML header, or footer, to put the Javascript code to the bottom of the body.

    Add a new Web Analytics solution

    In this section it's described how you can add support to a different Web Analytics solution.

    • override the richie/web_analytics.html template
    • define the WEB_ANALYTICS_ID setting with your tracking identification
    • define the WEB_ANALYTICS_PROVIDER setting with a value that represents your solution, eg. my-custom-web-analytics-software
    • optionally change WEB_ANALYTICS_LOCATION setting with head (default) or footer value
    • Example of a richie/web_analytics.html file customization that prints to the browser console log the dimension keys and values:
    <script type="text/javascript">
    {% for dimension_key, dimension_value_list in WEB_ANALYTICS.DIMENSIONS.items %}
    console.log("dimension: index '{{forloop.counter}}' with key '{{ dimension_key }}' with value '{{ dimension_value_list|join:" | " }}'");
    {% endfor %}
    </script>

    Output:

    dimension: index '1' with key 'organizations_codes' with value 'COMPATIBLE-EVEN-KEELED-UTILIZATION-19 | FOCUSED-NEXT-GENERATION-FUNCTIONALITIES-22 | UNIVERSAL-MODULAR-LOCAL-AREA-NETWORK-23'
    dimension: index '2' with key 'course_code' with value '00017'
    dimension: index '3' with key 'course_runs_titles' with value 'Run 0'
    dimension: index '4' with key 'course_runs_resource_links' with value ''
    dimension: index '5' with key 'page_title' with value 'Business-focused zero-defect application'

    But you can also contribute to Richie by creating a pull request to add support for a different web analytics solution. In this last case, you have to edit directly the richie/web_analytics.html template.

    Example of an override of the richie/web_analytics.html file:

    {% block web_analytics %}
    {% if WEB_ANALYTICS_ID %}
    {% if WEB_ANALYTICS_PROVIDER == "my_custom_web_analytics_software" %}
    <script type="text/javascript" src="{% static 'myapp/js/custom_web_analytics_software.js' %}">
    <script type="text/javascript">
    // javascript code that startups the custom web analytics software
    </script>
    {% endif %}
    {% endif %}
    {% endblock web_analytics %}

    The web analytics dimensions are being added to the django context using the WEB_ANALYTICS.DIMENSIONS dictionary. Because each dimension value could have multiple values, then each dictionary value is a list. Web analytics dimensions dictionary keys:

    • organizations_codes
    • course_code
    • course_runs_titles
    • course_runs_resource_links
    • page_title

    Example, if you only need the organization codes on your custom richie/web_analytics.html file:

    <script type="text/javascript">
    console.log("organization codes: '{{ WEB_ANALYTICS.DIMENSIONS.organizations_codes |join:" | " }}");
    </script>

    The frontend code also sends events to the web analytics provider. Richie sends events when the user is enrolled on a course run. To support different providers, you need to create a similar file of src/frontend/js/utils/api/web-analytics/google_analytics.ts and change the src/frontend/js/utils/api/web-analytics/index.ts file to include that newer provider.

    - - + + \ No newline at end of file diff --git a/docs/2.17.0/accessibility-testing/index.html b/docs/2.17.0/accessibility-testing/index.html index e1c09ae444..198a5e1d0e 100644 --- a/docs/2.17.0/accessibility-testing/index.html +++ b/docs/2.17.0/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.17.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.17.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.17.0/api/course-run-synchronization-api/index.html b/docs/2.17.0/api/course-run-synchronization-api/index.html index 2d008ac200..e89b3ad6c2 100644 --- a/docs/2.17.0/api/course-run-synchronization-api/index.html +++ b/docs/2.17.0/api/course-run-synchronization-api/index.html @@ -4,12 +4,12 @@ Course run synchronization API | Richie - - + +
    -
    Version: 2.17.0

    Course run synchronization API

    API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

    Synchronization endpoint [/api/1.0/course-runs-sync]

    This documentation describes version "1.0" of this API endpoint.

    Synchronize a course run [POST]

    It takes a JSON object containing the course run details:

    • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - +
      Version: 2.17.0

      Course run synchronization API

      API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

      Synchronization endpoint [/api/1.0/course-runs-sync]

      This documentation describes version "1.0" of this API endpoint.

      Synchronize a course run [POST]

      It takes a JSON object containing the course run details:

      • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - url of the course syllabus on the LMS from which a unique course identifier can be extracted
      • start: 2018-02-01T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course starts
      • end: 2018-02-28T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course ends
      • enrollment_start: 2018-01-01T06:00:00Z (string, optional) - ISO 8601 date, when enrollment @@ -19,7 +19,7 @@ containing the digest of the utf-8 encoded json representation of the submitted data for the given secret key and SHA256 digest algorithm (see [synchronizing-course-runs] for an example).
    • Body

        {
      "resource_link": "https://lms.example.com/courses/course-v1:001+001+001/info",
      "start": "2021-02-01T00:00:00Z",
      "end": "2021-02-31T23:59:59Z",
      "enrollment_start": "2021-01-01T00:00:00Z",
      "enrollment_end": "2021-01-31T23:59:59Z",
      "languages": ["en", "fr"]
      }
  • Response 200 (application/json)

    • Body

        {
      "success": True
      }
  • - - + + \ No newline at end of file diff --git a/docs/2.17.0/building-the-frontend/index.html b/docs/2.17.0/building-the-frontend/index.html index ce268b9416..bc958aac84 100644 --- a/docs/2.17.0/building-the-frontend/index.html +++ b/docs/2.17.0/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.17.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.17.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.17.0/contributing-guide/index.html b/docs/2.17.0/contributing-guide/index.html index 106a6d6de7..5bab6b17a4 100644 --- a/docs/2.17.0/contributing-guide/index.html +++ b/docs/2.17.0/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.17.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.17.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.17.0/cookiecutter/index.html b/docs/2.17.0/cookiecutter/index.html index 3bea584c1b..9637216e53 100644 --- a/docs/2.17.0/cookiecutter/index.html +++ b/docs/2.17.0/cookiecutter/index.html @@ -4,12 +4,12 @@ Start your own site | Richie - - + +
    -
    Version: 2.17.0

    Start your own site

    We use Cookiecutter to help you +

    Version: 2.17.0

    Start your own site

    We use Cookiecutter to help you set up a production-ready learning portal website based on Richie in seconds.

    Run Cookiecutter

    There are 2 options to run Cookiecutter:

    While you think of it, navigate to the directory in which you want to create your site factory:

    cd /path/to/your/code/directory

    If you chose to install Cookiecutter, you can now run it against our @@ -30,7 +30,7 @@ project's scaffolding but, don't worry, it will respect all the sites you will have created in the sites directory:

    cookiecutter --overwrite-if-exists gh:openfun/richie --directory=cookiecutter

    Help us improve this project

    After starting your project, please submit an issue let us know how it went and what other features we should add to make it better.

    - - + + \ No newline at end of file diff --git a/docs/2.17.0/css-guidelines/index.html b/docs/2.17.0/css-guidelines/index.html index 3cfb9e72a4..1f3062d80f 100644 --- a/docs/2.17.0/css-guidelines/index.html +++ b/docs/2.17.0/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.17.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.17.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.17.0/discover/index.html b/docs/2.17.0/discover/index.html index 80ac71d284..69e46437fb 100644 --- a/docs/2.17.0/discover/index.html +++ b/docs/2.17.0/discover/index.html @@ -4,12 +4,12 @@ Discover Richie | Richie - - + +
    -
    Version: 2.17.0

    Discover Richie

    Learning Management Systems (LMS) are great tools for hosting and playing interactive online +

    Version: 2.17.0

    Discover Richie

    Learning Management Systems (LMS) are great tools for hosting and playing interactive online courses and MOOCs.

    However, if you need to build a complete website with flexible content to aggregate your courses, in several languages and from different sources, you will soon need a CMS.

    At "France Université Numérique", we wanted to build an OpenSource portal with Python and Django. That's why we built Richie on top of DjangoCMS, one of @@ -21,7 +21,7 @@ OpenEdX Docker.

    Two users are available for testing:

    • admin: admin@example.com/admin
    • student: edx@example.com/edx

    The database is regularly flushed.

    Start your own site

    The next step after discovering Richie on the demo is to start your own project. We provide a production-ready template based on Cookiecutter that allows you to generate your project in seconds.

    Follow the guide on starting your own Richie site with Cookiecutter.

    Once you created a new site, you will be able to fully customize it:

    - - + + \ No newline at end of file diff --git a/docs/2.17.0/displaying-connection-status/index.html b/docs/2.17.0/displaying-connection-status/index.html index 2df985d0f6..e2b2e2e868 100644 --- a/docs/2.17.0/displaying-connection-status/index.html +++ b/docs/2.17.0/displaying-connection-status/index.html @@ -4,12 +4,12 @@ Displaying OpenEdX connection status in Richie | Richie - - + +
    -
    Version: 2.17.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status +

    Version: 2.17.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status and display profile information for the logged-in user in Richie.

    In Richie, the only users that are actually authenticated on the DjangoCMS instance producing the site, are editors and staff users. Your instructors and learners will not have user accounts on Richie, but authentication is delegated to a remote service that can be OpenEdX, KeyCloack, or @@ -26,7 +26,7 @@ set for the above authentication delegation BASE_URL setting.

    If you need to bind user data into a url, wrap the property between brackets. For example, the link configured above for the profile page {base_url:s}/u/(username) would point to https://lms.example.com/u/johndoe for a user carrying the username johndoe.

    - - + + \ No newline at end of file diff --git a/docs/2.17.0/django-react-interop/index.html b/docs/2.17.0/django-react-interop/index.html index a56322317f..6fa50d921d 100644 --- a/docs/2.17.0/django-react-interop/index.html +++ b/docs/2.17.0/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.17.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.17.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.17.0/docker-development/index.html b/docs/2.17.0/docker-development/index.html index bdfeb78247..3c1d3fafe8 100644 --- a/docs/2.17.0/docker-development/index.html +++ b/docs/2.17.0/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.17.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.17.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.17.0/filters-customization/index.html b/docs/2.17.0/filters-customization/index.html index 24392f862f..ff8dbc1a2e 100644 --- a/docs/2.17.0/filters-customization/index.html +++ b/docs/2.17.0/filters-customization/index.html @@ -4,12 +4,12 @@ Customizing search filters | Richie - - + +
    -
    Version: 2.17.0

    Customizing search filters

    You may want to customize the filters on the left side bar of the search page.

    Richie makes it easy to choose which filters you want to display among the existing filters +

    Version: 2.17.0

    Customizing search filters

    You may want to customize the filters on the left side bar of the search page.

    Richie makes it easy to choose which filters you want to display among the existing filters and in which order. You can also configure the existing filters to change their title or the way they behave. Lastly, you can completely override a filter or create your own custom filter from scratch.

    Filters configuration

    Filters must first be defined in the FILTERS_CONFIGURATION setting. It is a dictionary defining @@ -48,7 +48,7 @@ trivial, was given much care and includes many comments in an attempt to help writing new custom filters. Of course, don't hesitate to ask for help by opening an issue!

    - - + + \ No newline at end of file diff --git a/docs/2.17.0/frontend-overrides/index.html b/docs/2.17.0/frontend-overrides/index.html index 173c80906f..29cd664f36 100644 --- a/docs/2.17.0/frontend-overrides/index.html +++ b/docs/2.17.0/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.17.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.17.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.17.0/installation/index.html b/docs/2.17.0/installation/index.html index b7be1d5563..f9f8ce54d5 100644 --- a/docs/2.17.0/installation/index.html +++ b/docs/2.17.0/installation/index.html @@ -4,12 +4,12 @@ Installing Richie for development | Richie - - + +
    -
    Version: 2.17.0

    Installing Richie for development

    Richie is a container-native application but can also be installed +

    Version: 2.17.0

    Installing Richie for development

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of:

    • 3 running services:

      • database: postgresql or mysql at your preference,
      • elasticsearch: the search engine,
      • app: the actual DjangoCMS project with all our application code.
    • 2 containers for building purposes:

    - - + + \ No newline at end of file diff --git a/docs/2.17.0/internationalization/index.html b/docs/2.17.0/internationalization/index.html index e5dff2a8ee..dd78d5ad55 100644 --- a/docs/2.17.0/internationalization/index.html +++ b/docs/2.17.0/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.17.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.17.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.17.0/joanie-connection/index.html b/docs/2.17.0/joanie-connection/index.html index 2bfdfce104..3eaf4e712e 100644 --- a/docs/2.17.0/joanie-connection/index.html +++ b/docs/2.17.0/joanie-connection/index.html @@ -4,12 +4,12 @@ Joanie Connection | Richie - - + +
    -
    Version: 2.17.0

    Joanie Connection

    Settings

    All settings related to Joanie have to be declared in the JOANIE dictionary +

    Version: 2.17.0

    Joanie Connection

    Settings

    All settings related to Joanie have to be declared in the JOANIE dictionary within settings.py. To enable Joanie, the minimal configuration requires one property:

    • BASE_URL : the endpoint at which Joanie is accessible

    Add to your settings.py:

    ...
    JOANIE = {
    "BASE_URL": values.Value(environ_name="JOANIE_BASE_URL", environ_prefix=None)
    }
    ...

    Access Token

    Lifetime configuration

    Access Token is stored within the SessionStorage through react-query client persistor. @@ -19,7 +19,7 @@ lifetime of the access token defined by your authentication server. For example, if your authentication server is using djangorestframework-simplejwt to generate the access token, its lifetime is 5 minutes by default.

    - - + + \ No newline at end of file diff --git a/docs/2.17.0/lms-backends/index.html b/docs/2.17.0/lms-backends/index.html index c4c85a5848..1b0df10071 100644 --- a/docs/2.17.0/lms-backends/index.html +++ b/docs/2.17.0/lms-backends/index.html @@ -4,12 +4,12 @@ Configuring LMS Backends | Richie - - + +
    -
    Version: 2.17.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless +

    Version: 2.17.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless experience between browsing the course catalog on Richie, enrolling to a course and following the course itself on the LMS.

    It is possible to do the same with Moodle or any other LMS exposing an enrollment API, at the cost of writing a custom LMS handler backend.

    Prerequisites

    This connection requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that @@ -36,7 +36,7 @@ value other then manual.

    • Type: enum(string)
    • Required: No
    • Value: for example ["languages"]

    Technical support

    If you encounter an issue with this documentation or the backends included in Richie, please open an issue on our repository.

    If you need a custom backend, you can submit a PR or open an issue and we will consider adding it.

    - - + + \ No newline at end of file diff --git a/docs/2.17.0/lms-connection/index.html b/docs/2.17.0/lms-connection/index.html index dc51a9f38c..147bad1782 100644 --- a/docs/2.17.0/lms-connection/index.html +++ b/docs/2.17.0/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with one or more LMS | Richie - - + +
    -
    Version: 2.17.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated +

    Version: 2.17.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated seamless experience.

    As of today, each approach has been implemented for OpenEdX but the same could be done for other LMSes like Moodle, at the cost of minor adaptations.

    1. Displaying connection status

    OpenEdX can be configured to allow CORS requests. Doing so allows Richie to retrieve a user's connection status from OpenEdx and display the user's profile information directly on the Richie @@ -31,7 +31,7 @@ docker-compose.

    If you want to activate seamless enrollment locally for development, you will need to set up TLS domains for both Richie and OpenEdX. To do this, head over to our guide on setting-up TLS connections for Richie and OpenEdX.

    - - + + \ No newline at end of file diff --git a/docs/2.17.0/native-installation/index.html b/docs/2.17.0/native-installation/index.html index cd66fb5667..00255e4716 100644 --- a/docs/2.17.0/native-installation/index.html +++ b/docs/2.17.0/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.17.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.17.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native installation instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.17.0/synchronizing-course-runs/index.html b/docs/2.17.0/synchronizing-course-runs/index.html index 58888f860f..5682512692 100644 --- a/docs/2.17.0/synchronizing-course-runs/index.html +++ b/docs/2.17.0/synchronizing-course-runs/index.html @@ -4,12 +4,12 @@ Synchronizing course runs between Richie and OpenEdX | Richie - - + +
    -
    Version: 2.17.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the +

    Version: 2.17.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the RICHIE_COURSE_RUN_SYNC_SECRETS setting with one or more secrets:

    RICHIE_COURSE_RUN_SYNC_SECRETS = ["SharedSecret", "OtherSharedSecret"]

    This setting collects several secrets in order to allow rotating them without any downtime. Any of the secrets listed in this setting can be used to sign your queries.

    Your secret should be shared with the LMS or distant system that needs to synchronize its course runs with the Richie instance. Richie will try the declared secrets one by one until it finds @@ -29,7 +29,7 @@ course run is modified and published.

    Or you can use the Richie Open edX Synchronization which is based on the following code sample and also includes the enrollment count.

    Given a COURSE_HOOK setting defined as follows in your OpenEdX instance:

    COURSE_HOOK = {
    "secret": "SharedSecret",
    "url": "https://richie.example.com/api/v1.0/course-runs-sync/",
    }

    The code for the synchronization function in OpenEdX could look like this:

    import hashlib
    import hmac
    import json

    from django.conf import settings

    from microsite_configuration import microsite
    import requests
    from xmodule.modulestore.django import modulestore


    def update_course(course_key, *args, **kwargs):
    """Synchronize an OpenEdX course, identified by its course key, with a Richie instance."""
    course = modulestore().get_course(course_key)
    edxapp_domain = microsite.get_value("site_domain", settings.LMS_BASE)

    data = {
    "resource_link": "https://{:s}/courses/{!s}/info".format(
    edxapp_domain, course_key
    ),
    "start": course.start and course.start.isoformat(),
    "end": course.end and course.end.isoformat(),
    "enrollment_start": course.enrollment_start and course.enrollment_start.isoformat(),
    "enrollment_end": course.enrollment_end and course.enrollment_end.isoformat(),
    "languages": [course.language or settings.LANGUAGE_CODE],
    }

    signature = hmac.new(
    setting.COURSE_HOOK["secret"].encode("utf-8"),
    msg=json.dumps(data).encode("utf-8"),
    digestmod=hashlib.sha256,
    ).hexdigest()

    response = requests.post(
    setting.COURSE_HOOK["url"],
    json=data,
    headers={"Authorization": "SIG-HMAC-SHA256 {:s}".format(signature)},
    )

    Thanks to the signal emitted in OpenEdX, this function can then be triggered each time a course is modified and published:

    from django.dispatch import receiver
    from xmodule.modulestore.django import SignalHandler


    @receiver(SignalHandler.course_published, dispatch_uid='update_course_on_publish')
    def update_course_on_publish(sender, course_key, **kwargs):
    update_course(course_key)
    - - + + \ No newline at end of file diff --git a/docs/2.17.0/tls-connection/index.html b/docs/2.17.0/tls-connection/index.html index 1322fc0061..2937a3c3d0 100644 --- a/docs/2.17.0/tls-connection/index.html +++ b/docs/2.17.0/tls-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie and OpenEdX over TLS for development | Richie - - + +
    -
    Version: 2.17.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 +

    Version: 2.17.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 and uses a fake LMS backend (base.BaseLMSBackend) as you can see if you check the RICHIE_LMS_BACKENDS setting in env.d/development.

    This base backend uses session storage to fake enrollments to course runs.

    If you want to test real enrollments to an OpenEdX instance hosted on an external domain, OpenEdX will need to generate a CORS CSRF Cookie. This cookie is flagged as secure, which implies that @@ -32,7 +32,7 @@ to share cookies with the OpenEdx application to allow CORS CSRF Protected XHR requests.

    4. Start Richie and OpenEdx over SSL

    Now, the OpenEdx application should respond on https://edx.local.dev:8073, and Richie on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. The next time you want to use SSL, you can run the following command on both the Richie and OpenEdX projects:

    $ make run-ssl

    Of course, you can still run apps without ssl by using:

    $ make run
    - - + + \ No newline at end of file diff --git a/docs/2.17.0/web-analytics/index.html b/docs/2.17.0/web-analytics/index.html index 9bf195adda..64d27c90c4 100644 --- a/docs/2.17.0/web-analytics/index.html +++ b/docs/2.17.0/web-analytics/index.html @@ -4,19 +4,19 @@ Add web analytics to your site | Richie - - + +
    -
    Version: 2.17.0

    Add web analytics to your site

    Richie has native support to Google Analytics and Google Tag Manager Web Analytics solutions. +

    Version: 2.17.0

    Add web analytics to your site

    Richie has native support to Google Analytics and Google Tag Manager Web Analytics solutions. The purpose of this file is to explain how you can enable one of the supported Web Analytics providers and how you can extend Richie with an alternative solution.

    Google Analytics

    Next, it is described how you can configure the Google Analytics on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Analytics tracking id code.

    The current Google Analytics implementation also includes custom dimensions. Those dimensions permit you to create further analyses on Google Analytics or even use them to create custom reports. Custom dimensions with a value as example:

    • Organizations codes - UNIV_LISBON | UNIV_PORTO
    • Course code - COURSE_XPTO
    • Course runs titles - Summer edition | Winter edition
    • Course runs resource links - http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/info
    • Page title - Introduction to Programming

    Google Tag Manager

    Next, it is described how you can configure the Google Tag Manager on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Tag Manager tracking id code.
    • Add the WEB_ANALYTICS_PROVIDER setting with the google_tag_manager value.

    The current Google Tag Manager implementation also defines a custom dimensions like the Google Analytics.

    Location of the web analytics javascript

    Use the WEB_ANALYTICS_LOCATION settings to decide where do you want to put the Javascript code. Use head (default value), to put the Javascript on HTML header, or footer, to put the Javascript code to the bottom of the body.

    Add a new Web Analytics solution

    In this section it's described how you can add support to a different Web Analytics solution.

    • override the richie/web_analytics.html template
    • define the WEB_ANALYTICS_ID setting with your tracking identification
    • define the WEB_ANALYTICS_PROVIDER setting with a value that represents your solution, eg. my-custom-web-analytics-software
    • optionally change WEB_ANALYTICS_LOCATION setting with head (default) or footer value
    • Example of a richie/web_analytics.html file customization that prints to the browser console log the dimension keys and values:
    <script type="text/javascript">
    {% for dimension_key, dimension_value_list in WEB_ANALYTICS.DIMENSIONS.items %}
    console.log("dimension: index '{{forloop.counter}}' with key '{{ dimension_key }}' with value '{{ dimension_value_list|join:" | " }}'");
    {% endfor %}
    </script>

    Output:

    dimension: index '1' with key 'organizations_codes' with value 'COMPATIBLE-EVEN-KEELED-UTILIZATION-19 | FOCUSED-NEXT-GENERATION-FUNCTIONALITIES-22 | UNIVERSAL-MODULAR-LOCAL-AREA-NETWORK-23'
    dimension: index '2' with key 'course_code' with value '00017'
    dimension: index '3' with key 'course_runs_titles' with value 'Run 0'
    dimension: index '4' with key 'course_runs_resource_links' with value ''
    dimension: index '5' with key 'page_title' with value 'Business-focused zero-defect application'

    But you can also contribute to Richie by creating a pull request to add support for a different web analytics solution. In this last case, you have to edit directly the richie/web_analytics.html template.

    Example of an override of the richie/web_analytics.html file:

    {% block web_analytics %}
    {% if WEB_ANALYTICS_ID %}
    {% if WEB_ANALYTICS_PROVIDER == "my_custom_web_analytics_software" %}
    <script type="text/javascript" src="{% static 'myapp/js/custom_web_analytics_software.js' %}">
    <script type="text/javascript">
    // javascript code that startups the custom web analytics software
    </script>
    {% endif %}
    {% endif %}
    {% endblock web_analytics %}

    The web analytics dimensions are being added to the django context using the WEB_ANALYTICS.DIMENSIONS dictionary. Because each dimension value could have multiple values, then each dictionary value is a list. Web analytics dimensions dictionary keys:

    • organizations_codes
    • course_code
    • course_runs_titles
    • course_runs_resource_links
    • page_title

    Example, if you only need the organization codes on your custom richie/web_analytics.html file:

    <script type="text/javascript">
    console.log("organization codes: '{{ WEB_ANALYTICS.DIMENSIONS.organizations_codes |join:" | " }}");
    </script>

    The frontend code also sends events to the web analytics provider. Richie sends events when the user is enrolled on a course run. To support different providers, you need to create a similar file of src/frontend/js/utils/api/web-analytics/google_analytics.ts and change the src/frontend/js/utils/api/web-analytics/index.ts file to include that newer provider.

    - - + + \ No newline at end of file diff --git a/docs/2.18.0/accessibility-testing/index.html b/docs/2.18.0/accessibility-testing/index.html index 0fb11dfbd1..84f58c24ee 100644 --- a/docs/2.18.0/accessibility-testing/index.html +++ b/docs/2.18.0/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.18.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.18.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.18.0/api/course-run-synchronization-api/index.html b/docs/2.18.0/api/course-run-synchronization-api/index.html index d7feb24f35..015316eaa2 100644 --- a/docs/2.18.0/api/course-run-synchronization-api/index.html +++ b/docs/2.18.0/api/course-run-synchronization-api/index.html @@ -4,12 +4,12 @@ Course run synchronization API | Richie - - + +
    -
    Version: 2.18.0

    Course run synchronization API

    API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

    Synchronization endpoint [/api/1.0/course-runs-sync]

    This documentation describes version "1.0" of this API endpoint.

    Synchronize a course run [POST]

    It takes a JSON object containing the course run details:

    • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - +
      Version: 2.18.0

      Course run synchronization API

      API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

      Synchronization endpoint [/api/1.0/course-runs-sync]

      This documentation describes version "1.0" of this API endpoint.

      Synchronize a course run [POST]

      It takes a JSON object containing the course run details:

      • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - url of the course syllabus on the LMS from which a unique course identifier can be extracted
      • start: 2018-02-01T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course starts
      • end: 2018-02-28T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course ends
      • enrollment_start: 2018-01-01T06:00:00Z (string, optional) - ISO 8601 date, when enrollment @@ -19,7 +19,7 @@ containing the digest of the utf-8 encoded json representation of the submitted data for the given secret key and SHA256 digest algorithm (see [synchronizing-course-runs] for an example).
    • Body

        {
      "resource_link": "https://lms.example.com/courses/course-v1:001+001+001/info",
      "start": "2021-02-01T00:00:00Z",
      "end": "2021-02-31T23:59:59Z",
      "enrollment_start": "2021-01-01T00:00:00Z",
      "enrollment_end": "2021-01-31T23:59:59Z",
      "languages": ["en", "fr"]
      }
  • Response 200 (application/json)

    • Body

        {
      "success": True
      }
  • - - + + \ No newline at end of file diff --git a/docs/2.18.0/building-the-frontend/index.html b/docs/2.18.0/building-the-frontend/index.html index 13d221fe23..831571a862 100644 --- a/docs/2.18.0/building-the-frontend/index.html +++ b/docs/2.18.0/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.18.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.18.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.18.0/contributing-guide/index.html b/docs/2.18.0/contributing-guide/index.html index 627441c796..24e19de420 100644 --- a/docs/2.18.0/contributing-guide/index.html +++ b/docs/2.18.0/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.18.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.18.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.18.0/cookiecutter/index.html b/docs/2.18.0/cookiecutter/index.html index b941c46599..8605a8e8c1 100644 --- a/docs/2.18.0/cookiecutter/index.html +++ b/docs/2.18.0/cookiecutter/index.html @@ -4,12 +4,12 @@ Start your own site | Richie - - + +
    -
    Version: 2.18.0

    Start your own site

    We use Cookiecutter to help you +

    Version: 2.18.0

    Start your own site

    We use Cookiecutter to help you set up a production-ready learning portal website based on Richie in seconds.

    Run Cookiecutter

    There are 2 options to run Cookiecutter:

    While you think of it, navigate to the directory in which you want to create your site factory:

    cd /path/to/your/code/directory

    If you chose to install Cookiecutter, you can now run it against our @@ -30,7 +30,7 @@ project's scaffolding but, don't worry, it will respect all the sites you will have created in the sites directory:

    cookiecutter --overwrite-if-exists gh:openfun/richie --directory=cookiecutter

    Help us improve this project

    After starting your project, please submit an issue let us know how it went and what other features we should add to make it better.

    - - + + \ No newline at end of file diff --git a/docs/2.18.0/css-guidelines/index.html b/docs/2.18.0/css-guidelines/index.html index 995de3b4a2..2306269303 100644 --- a/docs/2.18.0/css-guidelines/index.html +++ b/docs/2.18.0/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.18.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.18.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.18.0/discover/index.html b/docs/2.18.0/discover/index.html index 7411121da8..be41a20929 100644 --- a/docs/2.18.0/discover/index.html +++ b/docs/2.18.0/discover/index.html @@ -4,12 +4,12 @@ Discover Richie | Richie - - + +
    -
    Version: 2.18.0

    Discover Richie

    Learning Management Systems (LMS) are great tools for hosting and playing interactive online +

    Version: 2.18.0

    Discover Richie

    Learning Management Systems (LMS) are great tools for hosting and playing interactive online courses and MOOCs.

    However, if you need to build a complete website with flexible content to aggregate your courses, in several languages and from different sources, you will soon need a CMS.

    At "France Université Numérique", we wanted to build an OpenSource portal with Python and Django. That's why we built Richie on top of DjangoCMS, one of @@ -21,7 +21,7 @@ OpenEdX Docker.

    Two users are available for testing:

    • admin: admin@example.com/admin
    • student: edx@example.com/edx

    The database is regularly flushed.

    Start your own site

    The next step after discovering Richie on the demo is to start your own project. We provide a production-ready template based on Cookiecutter that allows you to generate your project in seconds.

    Follow the guide on starting your own Richie site with Cookiecutter.

    Once you created a new site, you will be able to fully customize it:

    - - + + \ No newline at end of file diff --git a/docs/2.18.0/displaying-connection-status/index.html b/docs/2.18.0/displaying-connection-status/index.html index f5ebef476c..0c4a3c2acb 100644 --- a/docs/2.18.0/displaying-connection-status/index.html +++ b/docs/2.18.0/displaying-connection-status/index.html @@ -4,12 +4,12 @@ Displaying OpenEdX connection status in Richie | Richie - - + +
    -
    Version: 2.18.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status +

    Version: 2.18.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status and display profile information for the logged-in user in Richie.

    In Richie, the only users that are actually authenticated on the DjangoCMS instance producing the site, are editors and staff users. Your instructors and learners will not have user accounts on Richie, but authentication is delegated to a remote service that can be OpenEdX, KeyCloack, or @@ -26,7 +26,7 @@ set for the above authentication delegation BASE_URL setting.

    If you need to bind user data into a url, wrap the property between brackets. For example, the link configured above for the profile page {base_url:s}/u/(username) would point to https://lms.example.com/u/johndoe for a user carrying the username johndoe.

    - - + + \ No newline at end of file diff --git a/docs/2.18.0/django-react-interop/index.html b/docs/2.18.0/django-react-interop/index.html index 01fef43a55..feb4255c74 100644 --- a/docs/2.18.0/django-react-interop/index.html +++ b/docs/2.18.0/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.18.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.18.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.18.0/docker-development/index.html b/docs/2.18.0/docker-development/index.html index c9d0036cfd..abf38db947 100644 --- a/docs/2.18.0/docker-development/index.html +++ b/docs/2.18.0/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.18.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.18.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.18.0/filters-customization/index.html b/docs/2.18.0/filters-customization/index.html index 729fb25974..0169a499b6 100644 --- a/docs/2.18.0/filters-customization/index.html +++ b/docs/2.18.0/filters-customization/index.html @@ -4,12 +4,12 @@ Customizing search filters | Richie - - + +
    -
    Version: 2.18.0

    Customizing search filters

    You may want to customize the filters on the left side bar of the search page.

    Richie makes it easy to choose which filters you want to display among the existing filters +

    Version: 2.18.0

    Customizing search filters

    You may want to customize the filters on the left side bar of the search page.

    Richie makes it easy to choose which filters you want to display among the existing filters and in which order. You can also configure the existing filters to change their title or the way they behave. Lastly, you can completely override a filter or create your own custom filter from scratch.

    Filters configuration

    Filters must first be defined in the FILTERS_CONFIGURATION setting. It is a dictionary defining @@ -48,7 +48,7 @@ trivial, was given much care and includes many comments in an attempt to help writing new custom filters. Of course, don't hesitate to ask for help by opening an issue!

    - - + + \ No newline at end of file diff --git a/docs/2.18.0/frontend-overrides/index.html b/docs/2.18.0/frontend-overrides/index.html index acf485ff19..031d44f01c 100644 --- a/docs/2.18.0/frontend-overrides/index.html +++ b/docs/2.18.0/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.18.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.18.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.18.0/installation/index.html b/docs/2.18.0/installation/index.html index 6ce24bfec9..e3f54c7478 100644 --- a/docs/2.18.0/installation/index.html +++ b/docs/2.18.0/installation/index.html @@ -4,12 +4,12 @@ Installing Richie for development | Richie - - + +
    -
    Version: 2.18.0

    Installing Richie for development

    Richie is a container-native application but can also be installed +

    Version: 2.18.0

    Installing Richie for development

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of:

    • 3 running services:

      • database: postgresql or mysql at your preference,
      • elasticsearch: the search engine,
      • app: the actual DjangoCMS project with all our application code.
    • 2 containers for building purposes:

    - - + + \ No newline at end of file diff --git a/docs/2.18.0/internationalization/index.html b/docs/2.18.0/internationalization/index.html index 702a5a4808..2d707511b9 100644 --- a/docs/2.18.0/internationalization/index.html +++ b/docs/2.18.0/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.18.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.18.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.18.0/joanie-connection/index.html b/docs/2.18.0/joanie-connection/index.html index 89980cfef4..09c56826ff 100644 --- a/docs/2.18.0/joanie-connection/index.html +++ b/docs/2.18.0/joanie-connection/index.html @@ -4,12 +4,12 @@ Joanie Connection | Richie - - + +
    -
    Version: 2.18.0

    Joanie Connection

    Settings

    All settings related to Joanie have to be declared in the JOANIE_BACKEND dictionary +

    Version: 2.18.0

    Joanie Connection

    Settings

    All settings related to Joanie have to be declared in the JOANIE_BACKEND dictionary within settings.py. To enable Joanie, the minimal configuration requires following properties:

    • BASE_URL
    • BACKEND
    • COURSE_REGEX
    • JS_COURSE_REGEX

    Take a look to LMS Backend documentation to get details about those properties.

    Add to your settings.py:

    ...
    JOANIE_BACKEND = {
    "BASE_URL": values.Value(environ_name="JOANIE_BASE_URL", environ_prefix=None),
    "BACKEND": values.Value("richie.apps.courses.lms.joanie.JoanieBackend", environ_name="JOANIE_BACKEND", environ_prefix=None),
    "JS_BACKEND": values.Value("joanie", environ_name="JOANIE_JS_BACKEND", environ_prefix=None),
    "COURSE_REGEX": values.Value(
    r"^.*/api/(?P<resource_type>(course-runs|products))/(?P<resource_id>.*)/?$",
    environ_name="JOANIE_COURSE_REGEX",
    environ_prefix=None,
    ),
    "JS_COURSE_REGEX": values.Value(
    r"^.*/api/(course-runs|products)/(.*)/?$",
    environ_name="JOANIE_JS_COURSE_REGEX",
    environ_prefix=None,
    ),
    }
    ...

    Access Token

    Lifetime configuration

    Access Token is stored within the SessionStorage through react-query client persister. By default, richie frontend considered access token as stale after 5 minutes. You can change this @@ -18,7 +18,7 @@ lifetime of the access token defined by your authentication server. For example, if your authentication server is using djangorestframework-simplejwt to generate the access token, its lifetime is 5 minutes by default.

    - - + + \ No newline at end of file diff --git a/docs/2.18.0/lms-backends/index.html b/docs/2.18.0/lms-backends/index.html index 9bdf9a1c2e..363c680d57 100644 --- a/docs/2.18.0/lms-backends/index.html +++ b/docs/2.18.0/lms-backends/index.html @@ -4,12 +4,12 @@ Configuring LMS Backends | Richie - - + +
    -
    Version: 2.18.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless +

    Version: 2.18.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless experience between browsing the course catalog on Richie, enrolling to a course and following the course itself on the LMS.

    It is possible to do the same with Moodle or any other LMS exposing an enrollment API, at the cost of writing a custom LMS handler backend.

    Prerequisites

    This connection requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that @@ -36,7 +36,7 @@ value other then manual.

    • Type: enum(string)
    • Required: No
    • Value: for example ["languages"]

    Technical support

    If you encounter an issue with this documentation or the backends included in Richie, please open an issue on our repository.

    If you need a custom backend, you can submit a PR or open an issue and we will consider adding it.

    - - + + \ No newline at end of file diff --git a/docs/2.18.0/lms-connection/index.html b/docs/2.18.0/lms-connection/index.html index 64613e443b..02a752e848 100644 --- a/docs/2.18.0/lms-connection/index.html +++ b/docs/2.18.0/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with one or more LMS | Richie - - + +
    -
    Version: 2.18.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated +

    Version: 2.18.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated seamless experience.

    As of today, each approach has been implemented for OpenEdX but the same could be done for other LMSes like Moodle, at the cost of minor adaptations.

    1. Displaying connection status

    OpenEdX can be configured to allow CORS requests. Doing so allows Richie to retrieve a user's connection status from OpenEdx and display the user's profile information directly on the Richie @@ -31,7 +31,7 @@ docker-compose.

    If you want to activate seamless enrollment locally for development, you will need to set up TLS domains for both Richie and OpenEdX. To do this, head over to our guide on setting-up TLS connections for Richie and OpenEdX.

    - - + + \ No newline at end of file diff --git a/docs/2.18.0/native-installation/index.html b/docs/2.18.0/native-installation/index.html index 5480485cb7..769bb53944 100644 --- a/docs/2.18.0/native-installation/index.html +++ b/docs/2.18.0/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.18.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.18.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native installation instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.18.0/synchronizing-course-runs/index.html b/docs/2.18.0/synchronizing-course-runs/index.html index 92214d3118..bb86b68929 100644 --- a/docs/2.18.0/synchronizing-course-runs/index.html +++ b/docs/2.18.0/synchronizing-course-runs/index.html @@ -4,12 +4,12 @@ Synchronizing course runs between Richie and OpenEdX | Richie - - + +
    -
    Version: 2.18.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the +

    Version: 2.18.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the RICHIE_COURSE_RUN_SYNC_SECRETS setting with one or more secrets:

    RICHIE_COURSE_RUN_SYNC_SECRETS = ["SharedSecret", "OtherSharedSecret"]

    This setting collects several secrets in order to allow rotating them without any downtime. Any of the secrets listed in this setting can be used to sign your queries.

    Your secret should be shared with the LMS or distant system that needs to synchronize its course runs with the Richie instance. Richie will try the declared secrets one by one until it finds @@ -29,7 +29,7 @@ course run is modified and published.

    Or you can use the Richie Open edX Synchronization which is based on the following code sample and also includes the enrollment count.

    Given a COURSE_HOOK setting defined as follows in your OpenEdX instance:

    COURSE_HOOK = {
    "secret": "SharedSecret",
    "url": "https://richie.example.com/api/v1.0/course-runs-sync/",
    }

    The code for the synchronization function in OpenEdX could look like this:

    import hashlib
    import hmac
    import json

    from django.conf import settings

    from microsite_configuration import microsite
    import requests
    from xmodule.modulestore.django import modulestore


    def update_course(course_key, *args, **kwargs):
    """Synchronize an OpenEdX course, identified by its course key, with a Richie instance."""
    course = modulestore().get_course(course_key)
    edxapp_domain = microsite.get_value("site_domain", settings.LMS_BASE)

    data = {
    "resource_link": "https://{:s}/courses/{!s}/info".format(
    edxapp_domain, course_key
    ),
    "start": course.start and course.start.isoformat(),
    "end": course.end and course.end.isoformat(),
    "enrollment_start": course.enrollment_start and course.enrollment_start.isoformat(),
    "enrollment_end": course.enrollment_end and course.enrollment_end.isoformat(),
    "languages": [course.language or settings.LANGUAGE_CODE],
    }

    signature = hmac.new(
    setting.COURSE_HOOK["secret"].encode("utf-8"),
    msg=json.dumps(data).encode("utf-8"),
    digestmod=hashlib.sha256,
    ).hexdigest()

    response = requests.post(
    setting.COURSE_HOOK["url"],
    json=data,
    headers={"Authorization": "SIG-HMAC-SHA256 {:s}".format(signature)},
    )

    Thanks to the signal emitted in OpenEdX, this function can then be triggered each time a course is modified and published:

    from django.dispatch import receiver
    from xmodule.modulestore.django import SignalHandler


    @receiver(SignalHandler.course_published, dispatch_uid='update_course_on_publish')
    def update_course_on_publish(sender, course_key, **kwargs):
    update_course(course_key)
    - - + + \ No newline at end of file diff --git a/docs/2.18.0/tls-connection/index.html b/docs/2.18.0/tls-connection/index.html index 1f3153975d..5a63de2d29 100644 --- a/docs/2.18.0/tls-connection/index.html +++ b/docs/2.18.0/tls-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie and OpenEdX over TLS for development | Richie - - + +
    -
    Version: 2.18.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 +

    Version: 2.18.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 and uses a fake LMS backend (base.BaseLMSBackend) as you can see if you check the RICHIE_LMS_BACKENDS setting in env.d/development.

    This base backend uses session storage to fake enrollments to course runs.

    If you want to test real enrollments to an OpenEdX instance hosted on an external domain, OpenEdX will need to generate a CORS CSRF Cookie. This cookie is flagged as secure, which implies that @@ -32,7 +32,7 @@ to share cookies with the OpenEdx application to allow CORS CSRF Protected XHR requests.

    4. Start Richie and OpenEdx over SSL

    Now, the OpenEdx application should respond on https://edx.local.dev:8073, and Richie on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. The next time you want to use SSL, you can run the following command on both the Richie and OpenEdX projects:

    $ make run-ssl

    Of course, you can still run apps without ssl by using:

    $ make run
    - - + + \ No newline at end of file diff --git a/docs/2.18.0/web-analytics/index.html b/docs/2.18.0/web-analytics/index.html index 0623e7e87d..e7148f9a17 100644 --- a/docs/2.18.0/web-analytics/index.html +++ b/docs/2.18.0/web-analytics/index.html @@ -4,19 +4,19 @@ Add web analytics to your site | Richie - - + +
    -
    Version: 2.18.0

    Add web analytics to your site

    Richie has native support to Google Analytics and Google Tag Manager Web Analytics solutions. +

    Version: 2.18.0

    Add web analytics to your site

    Richie has native support to Google Analytics and Google Tag Manager Web Analytics solutions. The purpose of this file is to explain how you can enable one of the supported Web Analytics providers and how you can extend Richie with an alternative solution.

    Google Analytics

    Next, it is described how you can configure the Google Analytics on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Analytics tracking id code.

    The current Google Analytics implementation also includes custom dimensions. Those dimensions permit you to create further analyses on Google Analytics or even use them to create custom reports. Custom dimensions with a value as example:

    • Organizations codes - UNIV_LISBON | UNIV_PORTO
    • Course code - COURSE_XPTO
    • Course runs titles - Summer edition | Winter edition
    • Course runs resource links - http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/info
    • Page title - Introduction to Programming

    Google Tag Manager

    Next, it is described how you can configure the Google Tag Manager on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Tag Manager tracking id code.
    • Add the WEB_ANALYTICS_PROVIDER setting with the google_tag_manager value.

    The current Google Tag Manager implementation also defines a custom dimensions like the Google Analytics.

    Location of the web analytics javascript

    Use the WEB_ANALYTICS_LOCATION settings to decide where do you want to put the Javascript code. Use head (default value), to put the Javascript on HTML header, or footer, to put the Javascript code to the bottom of the body.

    Add a new Web Analytics solution

    In this section it's described how you can add support to a different Web Analytics solution.

    • override the richie/web_analytics.html template
    • define the WEB_ANALYTICS_ID setting with your tracking identification
    • define the WEB_ANALYTICS_PROVIDER setting with a value that represents your solution, eg. my-custom-web-analytics-software
    • optionally change WEB_ANALYTICS_LOCATION setting with head (default) or footer value
    • Example of a richie/web_analytics.html file customization that prints to the browser console log the dimension keys and values:
    <script type="text/javascript">
    {% for dimension_key, dimension_value_list in WEB_ANALYTICS.DIMENSIONS.items %}
    console.log("dimension: index '{{forloop.counter}}' with key '{{ dimension_key }}' with value '{{ dimension_value_list|join:" | " }}'");
    {% endfor %}
    </script>

    Output:

    dimension: index '1' with key 'organizations_codes' with value 'COMPATIBLE-EVEN-KEELED-UTILIZATION-19 | FOCUSED-NEXT-GENERATION-FUNCTIONALITIES-22 | UNIVERSAL-MODULAR-LOCAL-AREA-NETWORK-23'
    dimension: index '2' with key 'course_code' with value '00017'
    dimension: index '3' with key 'course_runs_titles' with value 'Run 0'
    dimension: index '4' with key 'course_runs_resource_links' with value ''
    dimension: index '5' with key 'page_title' with value 'Business-focused zero-defect application'

    But you can also contribute to Richie by creating a pull request to add support for a different web analytics solution. In this last case, you have to edit directly the richie/web_analytics.html template.

    Example of an override of the richie/web_analytics.html file:

    {% block web_analytics %}
    {% if WEB_ANALYTICS_ID %}
    {% if WEB_ANALYTICS_PROVIDER == "my_custom_web_analytics_software" %}
    <script type="text/javascript" src="{% static 'myapp/js/custom_web_analytics_software.js' %}">
    <script type="text/javascript">
    // javascript code that startups the custom web analytics software
    </script>
    {% endif %}
    {% endif %}
    {% endblock web_analytics %}

    The web analytics dimensions are being added to the django context using the WEB_ANALYTICS.DIMENSIONS dictionary. Because each dimension value could have multiple values, then each dictionary value is a list. Web analytics dimensions dictionary keys:

    • organizations_codes
    • course_code
    • course_runs_titles
    • course_runs_resource_links
    • page_title

    Example, if you only need the organization codes on your custom richie/web_analytics.html file:

    <script type="text/javascript">
    console.log("organization codes: '{{ WEB_ANALYTICS.DIMENSIONS.organizations_codes |join:" | " }}");
    </script>

    The frontend code also sends events to the web analytics provider. Richie sends events when the user is enrolled on a course run. To support different providers, you need to create a similar file of src/frontend/js/utils/api/web-analytics/google_analytics.ts and change the src/frontend/js/utils/api/web-analytics/index.ts file to include that newer provider.

    - - + + \ No newline at end of file diff --git a/docs/2.19.0/accessibility-testing/index.html b/docs/2.19.0/accessibility-testing/index.html index 9faa48eb16..5e5846fc4b 100644 --- a/docs/2.19.0/accessibility-testing/index.html +++ b/docs/2.19.0/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.19.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.19.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.19.0/api/course-run-synchronization-api/index.html b/docs/2.19.0/api/course-run-synchronization-api/index.html index 7836041d3f..d78fdf1c4a 100644 --- a/docs/2.19.0/api/course-run-synchronization-api/index.html +++ b/docs/2.19.0/api/course-run-synchronization-api/index.html @@ -4,12 +4,12 @@ Course run synchronization API | Richie - - + +
    -
    Version: 2.19.0

    Course run synchronization API

    API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

    Synchronization endpoint [/api/1.0/course-runs-sync]

    This documentation describes version "1.0" of this API endpoint.

    Synchronize a course run [POST]

    It takes a JSON object containing the course run details:

    • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - +
      Version: 2.19.0

      Course run synchronization API

      API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

      Synchronization endpoint [/api/1.0/course-runs-sync]

      This documentation describes version "1.0" of this API endpoint.

      Synchronize a course run [POST]

      It takes a JSON object containing the course run details:

      • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - url of the course syllabus on the LMS from which a unique course identifier can be extracted
      • start: 2018-02-01T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course starts
      • end: 2018-02-28T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course ends
      • enrollment_start: 2018-01-01T06:00:00Z (string, optional) - ISO 8601 date, when enrollment @@ -19,7 +19,7 @@ containing the digest of the utf-8 encoded json representation of the submitted data for the given secret key and SHA256 digest algorithm (see [synchronizing-course-runs] for an example).
    • Body

        {
      "resource_link": "https://lms.example.com/courses/course-v1:001+001+001/info",
      "start": "2021-02-01T00:00:00Z",
      "end": "2021-02-31T23:59:59Z",
      "enrollment_start": "2021-01-01T00:00:00Z",
      "enrollment_end": "2021-01-31T23:59:59Z",
      "languages": ["en", "fr"]
      }
  • Response 200 (application/json)

    • Body

        {
      "success": True
      }
  • - - + + \ No newline at end of file diff --git a/docs/2.19.0/building-the-frontend/index.html b/docs/2.19.0/building-the-frontend/index.html index 6b0cca344f..ee4675bba4 100644 --- a/docs/2.19.0/building-the-frontend/index.html +++ b/docs/2.19.0/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.19.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.19.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.19.0/contributing-guide/index.html b/docs/2.19.0/contributing-guide/index.html index 1d57801af0..9b601d594c 100644 --- a/docs/2.19.0/contributing-guide/index.html +++ b/docs/2.19.0/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.19.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.19.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.19.0/cookiecutter/index.html b/docs/2.19.0/cookiecutter/index.html index ea05009788..51ef18772c 100644 --- a/docs/2.19.0/cookiecutter/index.html +++ b/docs/2.19.0/cookiecutter/index.html @@ -4,12 +4,12 @@ Start your own site | Richie - - + +
    -
    Version: 2.19.0

    Start your own site

    We use Cookiecutter to help you +

    Version: 2.19.0

    Start your own site

    We use Cookiecutter to help you set up a production-ready learning portal website based on Richie in seconds.

    Run Cookiecutter

    There are 2 options to run Cookiecutter:

    While you think of it, navigate to the directory in which you want to create your site factory:

    cd /path/to/your/code/directory

    If you chose to install Cookiecutter, you can now run it against our @@ -30,7 +30,7 @@ project's scaffolding but, don't worry, it will respect all the sites you will have created in the sites directory:

    cookiecutter --overwrite-if-exists gh:openfun/richie --directory=cookiecutter

    Help us improve this project

    After starting your project, please submit an issue let us know how it went and what other features we should add to make it better.

    - - + + \ No newline at end of file diff --git a/docs/2.19.0/css-guidelines/index.html b/docs/2.19.0/css-guidelines/index.html index 2815b7ad9b..7a0b8329fb 100644 --- a/docs/2.19.0/css-guidelines/index.html +++ b/docs/2.19.0/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.19.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.19.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.19.0/discover/index.html b/docs/2.19.0/discover/index.html index 544d9672ce..072e171e50 100644 --- a/docs/2.19.0/discover/index.html +++ b/docs/2.19.0/discover/index.html @@ -4,12 +4,12 @@ Discover Richie | Richie - - + +
    -
    Version: 2.19.0

    Discover Richie

    Learning Management Systems (LMS) are great tools for hosting and playing interactive online +

    Version: 2.19.0

    Discover Richie

    Learning Management Systems (LMS) are great tools for hosting and playing interactive online courses and MOOCs.

    However, if you need to build a complete website with flexible content to aggregate your courses, in several languages and from different sources, you will soon need a CMS.

    At "France Université Numérique", we wanted to build an OpenSource portal with Python and Django. That's why we built Richie on top of DjangoCMS, one of @@ -21,7 +21,7 @@ OpenEdX Docker.

    Two users are available for testing:

    • admin: admin@example.com/admin
    • student: edx@example.com/edx

    The database is regularly flushed.

    Start your own site

    The next step after discovering Richie on the demo is to start your own project. We provide a production-ready template based on Cookiecutter that allows you to generate your project in seconds.

    Follow the guide on starting your own Richie site with Cookiecutter.

    Once you created a new site, you will be able to fully customize it:

    - - + + \ No newline at end of file diff --git a/docs/2.19.0/displaying-connection-status/index.html b/docs/2.19.0/displaying-connection-status/index.html index 030d4ed0d6..4b54dc57d5 100644 --- a/docs/2.19.0/displaying-connection-status/index.html +++ b/docs/2.19.0/displaying-connection-status/index.html @@ -4,12 +4,12 @@ Displaying OpenEdX connection status in Richie | Richie - - + +
    -
    Version: 2.19.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status +

    Version: 2.19.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status and display profile information for the logged-in user in Richie.

    In Richie, the only users that are actually authenticated on the DjangoCMS instance producing the site, are editors and staff users. Your instructors and learners will not have user accounts on Richie, but authentication is delegated to a remote service that can be OpenEdX, KeyCloack, or @@ -26,7 +26,7 @@ set for the above authentication delegation BASE_URL setting.

    If you need to bind user data into a url, wrap the property between brackets. For example, the link configured above for the profile page {base_url:s}/u/(username) would point to https://lms.example.com/u/johndoe for a user carrying the username johndoe.

    - - + + \ No newline at end of file diff --git a/docs/2.19.0/django-react-interop/index.html b/docs/2.19.0/django-react-interop/index.html index 6fbaf76623..c6b9178211 100644 --- a/docs/2.19.0/django-react-interop/index.html +++ b/docs/2.19.0/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.19.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.19.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.19.0/docker-development/index.html b/docs/2.19.0/docker-development/index.html index c420d6b730..239f962db2 100644 --- a/docs/2.19.0/docker-development/index.html +++ b/docs/2.19.0/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.19.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.19.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.19.0/filters-customization/index.html b/docs/2.19.0/filters-customization/index.html index e08caddfd0..239f70a1e3 100644 --- a/docs/2.19.0/filters-customization/index.html +++ b/docs/2.19.0/filters-customization/index.html @@ -4,12 +4,12 @@ Customizing search filters | Richie - - + +
    -
    Version: 2.19.0

    Customizing search filters

    You may want to customize the filters on the left side bar of the search page.

    Richie makes it easy to choose which filters you want to display among the existing filters +

    Version: 2.19.0

    Customizing search filters

    You may want to customize the filters on the left side bar of the search page.

    Richie makes it easy to choose which filters you want to display among the existing filters and in which order. You can also configure the existing filters to change their title or the way they behave. Lastly, you can completely override a filter or create your own custom filter from scratch.

    Filters configuration

    Filters must first be defined in the FILTERS_CONFIGURATION setting. It is a dictionary defining @@ -48,7 +48,7 @@ trivial, was given much care and includes many comments in an attempt to help writing new custom filters. Of course, don't hesitate to ask for help by opening an issue!

    - - + + \ No newline at end of file diff --git a/docs/2.19.0/frontend-overrides/index.html b/docs/2.19.0/frontend-overrides/index.html index 17a7c823ab..0ae0f0d45b 100644 --- a/docs/2.19.0/frontend-overrides/index.html +++ b/docs/2.19.0/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.19.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.19.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.19.0/installation/index.html b/docs/2.19.0/installation/index.html index 2c51e3ba75..bbbc80e66e 100644 --- a/docs/2.19.0/installation/index.html +++ b/docs/2.19.0/installation/index.html @@ -4,12 +4,12 @@ Installing Richie for development | Richie - - + +
    -
    Version: 2.19.0

    Installing Richie for development

    Richie is a container-native application but can also be installed +

    Version: 2.19.0

    Installing Richie for development

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of:

    • 3 running services:

      • database: postgresql or mysql at your preference,
      • elasticsearch: the search engine,
      • app: the actual DjangoCMS project with all our application code.
    • 2 containers for building purposes:

    - - + + \ No newline at end of file diff --git a/docs/2.19.0/internationalization/index.html b/docs/2.19.0/internationalization/index.html index cc2b350f5e..fbf01d0bfc 100644 --- a/docs/2.19.0/internationalization/index.html +++ b/docs/2.19.0/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.19.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.19.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.19.0/joanie-connection/index.html b/docs/2.19.0/joanie-connection/index.html index 6671c2f670..05fd43a8ba 100644 --- a/docs/2.19.0/joanie-connection/index.html +++ b/docs/2.19.0/joanie-connection/index.html @@ -4,12 +4,12 @@ Joanie Connection | Richie - - + +
    -
    Version: 2.19.0

    Joanie Connection

    Joanie delivers an API able to manage course +

    Version: 2.19.0

    Joanie Connection

    Joanie delivers an API able to manage course enrollment/subscription, payment and certificates delivery. Richie can be configured to display course runs and micro-credentials managed through Joanie.

    In fact, Richie treats Joanie almost like a LMS backend that's why settings are similars.

    Configuring Joanie

    All settings related to Joanie have to be declared in the JOANIE_BACKEND dictionnary @@ -40,7 +40,7 @@ authentication server is using djangorestframework-simplejwt to generate the access token, its lifetime is 5 minutes by default.

    Technical support

    If you encounter an issue with this documentation, please open an issue on our repository.

    - - + + \ No newline at end of file diff --git a/docs/2.19.0/lms-backends/index.html b/docs/2.19.0/lms-backends/index.html index 86b63aa818..bd870e499e 100644 --- a/docs/2.19.0/lms-backends/index.html +++ b/docs/2.19.0/lms-backends/index.html @@ -4,12 +4,12 @@ Configuring LMS Backends | Richie - - + +
    -
    Version: 2.19.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless +

    Version: 2.19.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless experience between browsing the course catalog on Richie, enrolling to a course and following the course itself on the LMS.

    It is possible to do the same with Moodle or any other LMS exposing an enrollment API, at the cost of writing a custom LMS handler backend.

    Prerequisites

    This connection requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that @@ -36,7 +36,7 @@ value other then manual.

    • Type: enum(string)
    • Required: No
    • Value: for example ["languages"]

    Technical support

    If you encounter an issue with this documentation or the backends included in Richie, please open an issue on our repository.

    If you need a custom backend, you can submit a PR or open an issue and we will consider adding it.

    - - + + \ No newline at end of file diff --git a/docs/2.19.0/lms-connection/index.html b/docs/2.19.0/lms-connection/index.html index e48b4b98ba..742a77ac67 100644 --- a/docs/2.19.0/lms-connection/index.html +++ b/docs/2.19.0/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with one or more LMS | Richie - - + +
    -
    Version: 2.19.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated +

    Version: 2.19.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated seamless experience.

    As of today, each approach has been implemented for OpenEdX but the same could be done for other LMSes like Moodle, at the cost of minor adaptations.

    1. Displaying connection status

    OpenEdX can be configured to allow CORS requests. Doing so allows Richie to retrieve a user's connection status from OpenEdx and display the user's profile information directly on the Richie @@ -31,7 +31,7 @@ docker-compose.

    If you want to activate seamless enrollment locally for development, you will need to set up TLS domains for both Richie and OpenEdX. To do this, head over to our guide on setting-up TLS connections for Richie and OpenEdX.

    - - + + \ No newline at end of file diff --git a/docs/2.19.0/native-installation/index.html b/docs/2.19.0/native-installation/index.html index c17ed15bc0..bb0bf9e525 100644 --- a/docs/2.19.0/native-installation/index.html +++ b/docs/2.19.0/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.19.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.19.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native installation instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.19.0/synchronizing-course-runs/index.html b/docs/2.19.0/synchronizing-course-runs/index.html index 4c250648a4..7b611d4423 100644 --- a/docs/2.19.0/synchronizing-course-runs/index.html +++ b/docs/2.19.0/synchronizing-course-runs/index.html @@ -4,12 +4,12 @@ Synchronizing course runs between Richie and OpenEdX | Richie - - + +
    -
    Version: 2.19.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the +

    Version: 2.19.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the RICHIE_COURSE_RUN_SYNC_SECRETS setting with one or more secrets:

    RICHIE_COURSE_RUN_SYNC_SECRETS = ["SharedSecret", "OtherSharedSecret"]

    This setting collects several secrets in order to allow rotating them without any downtime. Any of the secrets listed in this setting can be used to sign your queries.

    Your secret should be shared with the LMS or distant system that needs to synchronize its course runs with the Richie instance. Richie will try the declared secrets one by one until it finds @@ -29,7 +29,7 @@ course run is modified and published.

    Or you can use the Richie Open edX Synchronization which is based on the following code sample and also includes the enrollment count.

    Given a COURSE_HOOK setting defined as follows in your OpenEdX instance:

    COURSE_HOOK = {
    "secret": "SharedSecret",
    "url": "https://richie.example.com/api/v1.0/course-runs-sync/",
    }

    The code for the synchronization function in OpenEdX could look like this:

    import hashlib
    import hmac
    import json

    from django.conf import settings

    from microsite_configuration import microsite
    import requests
    from xmodule.modulestore.django import modulestore


    def update_course(course_key, *args, **kwargs):
    """Synchronize an OpenEdX course, identified by its course key, with a Richie instance."""
    course = modulestore().get_course(course_key)
    edxapp_domain = microsite.get_value("site_domain", settings.LMS_BASE)

    data = {
    "resource_link": "https://{:s}/courses/{!s}/info".format(
    edxapp_domain, course_key
    ),
    "start": course.start and course.start.isoformat(),
    "end": course.end and course.end.isoformat(),
    "enrollment_start": course.enrollment_start and course.enrollment_start.isoformat(),
    "enrollment_end": course.enrollment_end and course.enrollment_end.isoformat(),
    "languages": [course.language or settings.LANGUAGE_CODE],
    }

    signature = hmac.new(
    setting.COURSE_HOOK["secret"].encode("utf-8"),
    msg=json.dumps(data).encode("utf-8"),
    digestmod=hashlib.sha256,
    ).hexdigest()

    response = requests.post(
    setting.COURSE_HOOK["url"],
    json=data,
    headers={"Authorization": "SIG-HMAC-SHA256 {:s}".format(signature)},
    )

    Thanks to the signal emitted in OpenEdX, this function can then be triggered each time a course is modified and published:

    from django.dispatch import receiver
    from xmodule.modulestore.django import SignalHandler


    @receiver(SignalHandler.course_published, dispatch_uid='update_course_on_publish')
    def update_course_on_publish(sender, course_key, **kwargs):
    update_course(course_key)
    - - + + \ No newline at end of file diff --git a/docs/2.19.0/tls-connection/index.html b/docs/2.19.0/tls-connection/index.html index db478762cc..38eba55ba2 100644 --- a/docs/2.19.0/tls-connection/index.html +++ b/docs/2.19.0/tls-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie and OpenEdX over TLS for development | Richie - - + +
    -
    Version: 2.19.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 +

    Version: 2.19.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 and uses a fake LMS backend (base.BaseLMSBackend) as you can see if you check the RICHIE_LMS_BACKENDS setting in env.d/development.

    This base backend uses session storage to fake enrollments to course runs.

    If you want to test real enrollments to an OpenEdX instance hosted on an external domain, OpenEdX will need to generate a CORS CSRF Cookie. This cookie is flagged as secure, which implies that @@ -32,7 +32,7 @@ to share cookies with the OpenEdx application to allow CORS CSRF Protected XHR requests.

    4. Start Richie and OpenEdx over SSL

    Now, the OpenEdx application should respond on https://edx.local.dev:8073, and Richie on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. The next time you want to use SSL, you can run the following command on both the Richie and OpenEdX projects:

    $ make run-ssl

    Of course, you can still run apps without ssl by using:

    $ make run
    - - + + \ No newline at end of file diff --git a/docs/2.19.0/web-analytics/index.html b/docs/2.19.0/web-analytics/index.html index 98d98e8045..17dda30e85 100644 --- a/docs/2.19.0/web-analytics/index.html +++ b/docs/2.19.0/web-analytics/index.html @@ -4,19 +4,19 @@ Add web analytics to your site | Richie - - + +
    -
    Version: 2.19.0

    Add web analytics to your site

    Richie has native support to Google Analytics and Google Tag Manager Web Analytics solutions. +

    Version: 2.19.0

    Add web analytics to your site

    Richie has native support to Google Analytics and Google Tag Manager Web Analytics solutions. The purpose of this file is to explain how you can enable one of the supported Web Analytics providers and how you can extend Richie with an alternative solution.

    Google Analytics

    Next, it is described how you can configure the Google Analytics on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Analytics tracking id code.

    The current Google Analytics implementation also includes custom dimensions. Those dimensions permit you to create further analyses on Google Analytics or even use them to create custom reports. Custom dimensions with a value as example:

    • Organizations codes - UNIV_LISBON | UNIV_PORTO
    • Course code - COURSE_XPTO
    • Course runs titles - Summer edition | Winter edition
    • Course runs resource links - http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/info
    • Page title - Introduction to Programming

    Google Tag Manager

    Next, it is described how you can configure the Google Tag Manager on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Tag Manager tracking id code.
    • Add the WEB_ANALYTICS_PROVIDER setting with the google_tag_manager value.

    The current Google Tag Manager implementation also defines a custom dimensions like the Google Analytics.

    Location of the web analytics javascript

    Use the WEB_ANALYTICS_LOCATION settings to decide where do you want to put the Javascript code. Use head (default value), to put the Javascript on HTML header, or footer, to put the Javascript code to the bottom of the body.

    Add a new Web Analytics solution

    In this section it's described how you can add support to a different Web Analytics solution.

    • override the richie/web_analytics.html template
    • define the WEB_ANALYTICS_ID setting with your tracking identification
    • define the WEB_ANALYTICS_PROVIDER setting with a value that represents your solution, eg. my-custom-web-analytics-software
    • optionally change WEB_ANALYTICS_LOCATION setting with head (default) or footer value
    • Example of a richie/web_analytics.html file customization that prints to the browser console log the dimension keys and values:
    <script type="text/javascript">
    {% for dimension_key, dimension_value_list in WEB_ANALYTICS.DIMENSIONS.items %}
    console.log("dimension: index '{{forloop.counter}}' with key '{{ dimension_key }}' with value '{{ dimension_value_list|join:" | " }}'");
    {% endfor %}
    </script>

    Output:

    dimension: index '1' with key 'organizations_codes' with value 'COMPATIBLE-EVEN-KEELED-UTILIZATION-19 | FOCUSED-NEXT-GENERATION-FUNCTIONALITIES-22 | UNIVERSAL-MODULAR-LOCAL-AREA-NETWORK-23'
    dimension: index '2' with key 'course_code' with value '00017'
    dimension: index '3' with key 'course_runs_titles' with value 'Run 0'
    dimension: index '4' with key 'course_runs_resource_links' with value ''
    dimension: index '5' with key 'page_title' with value 'Business-focused zero-defect application'

    But you can also contribute to Richie by creating a pull request to add support for a different web analytics solution. In this last case, you have to edit directly the richie/web_analytics.html template.

    Example of an override of the richie/web_analytics.html file:

    {% block web_analytics %}
    {% if WEB_ANALYTICS_ID %}
    {% if WEB_ANALYTICS_PROVIDER == "my_custom_web_analytics_software" %}
    <script type="text/javascript" src="{% static 'myapp/js/custom_web_analytics_software.js' %}">
    <script type="text/javascript">
    // javascript code that startups the custom web analytics software
    </script>
    {% endif %}
    {% endif %}
    {% endblock web_analytics %}

    The web analytics dimensions are being added to the django context using the WEB_ANALYTICS.DIMENSIONS dictionary. Because each dimension value could have multiple values, then each dictionary value is a list. Web analytics dimensions dictionary keys:

    • organizations_codes
    • course_code
    • course_runs_titles
    • course_runs_resource_links
    • page_title

    Example, if you only need the organization codes on your custom richie/web_analytics.html file:

    <script type="text/javascript">
    console.log("organization codes: '{{ WEB_ANALYTICS.DIMENSIONS.organizations_codes |join:" | " }}");
    </script>

    The frontend code also sends events to the web analytics provider. Richie sends events when the user is enrolled on a course run. To support different providers, you need to create a similar file of src/frontend/js/utils/api/web-analytics/google_analytics.ts and change the src/frontend/js/utils/api/web-analytics/index.ts file to include that newer provider.

    - - + + \ No newline at end of file diff --git a/docs/2.2.0/accessibility-testing/index.html b/docs/2.2.0/accessibility-testing/index.html index 4c176f5b92..f1d0db80db 100644 --- a/docs/2.2.0/accessibility-testing/index.html +++ b/docs/2.2.0/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.2.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.2.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.2.0/building-the-frontend/index.html b/docs/2.2.0/building-the-frontend/index.html index 54ab5f86d4..3b5b53df23 100644 --- a/docs/2.2.0/building-the-frontend/index.html +++ b/docs/2.2.0/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.2.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.2.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.2.0/contributing-guide/index.html b/docs/2.2.0/contributing-guide/index.html index 9c0f68ec09..c5b52eb243 100644 --- a/docs/2.2.0/contributing-guide/index.html +++ b/docs/2.2.0/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.2.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.2.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.2.0/css-guidelines/index.html b/docs/2.2.0/css-guidelines/index.html index 71735d9d7a..fc4d134b00 100644 --- a/docs/2.2.0/css-guidelines/index.html +++ b/docs/2.2.0/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.2.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.2.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.2.0/discover/index.html b/docs/2.2.0/discover/index.html index d32dc1b042..20387ea9e6 100644 --- a/docs/2.2.0/discover/index.html +++ b/docs/2.2.0/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
    -
    Version: 2.2.0

    Getting started with Richie

    If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed +

    Version: 2.2.0

    Getting started with Richie

    If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of 4 services:

    • db: the Postgresql database,
    • elasticsearch: the search engine,
    • app: the actual DjangoCMS project with all our application code,
    • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

    At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -27,7 +27,7 @@ to bootstrap an instance.

    Just start apps with make run.

    Richie should respond on http://localhost:8070 and OpenEdx on http://localhost:8073.

    Advanced - Connecting Richie to OpenEdx

    If you want users to enroll on courses in OpenEdx directly from Richie via API calls, you should read the advanced guide to connect Richie to OpenEdx over TLS.

    - - + + \ No newline at end of file diff --git a/docs/2.2.0/django-react-interop/index.html b/docs/2.2.0/django-react-interop/index.html index 2df33011bc..6e09aa5d48 100644 --- a/docs/2.2.0/django-react-interop/index.html +++ b/docs/2.2.0/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.2.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.2.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.2.0/docker-development/index.html b/docs/2.2.0/docker-development/index.html index 19d10d602c..55f7c20c17 100644 --- a/docs/2.2.0/docker-development/index.html +++ b/docs/2.2.0/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.2.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.2.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.2.0/frontend-overrides/index.html b/docs/2.2.0/frontend-overrides/index.html index f57b7ef844..9b3c486da3 100644 --- a/docs/2.2.0/frontend-overrides/index.html +++ b/docs/2.2.0/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.2.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.2.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.2.0/internationalization/index.html b/docs/2.2.0/internationalization/index.html index 18907ab9b6..07c11531ee 100644 --- a/docs/2.2.0/internationalization/index.html +++ b/docs/2.2.0/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.2.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.2.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.2.0/lms-connection/index.html b/docs/2.2.0/lms-connection/index.html index 61f6d4ebd8..7356a82a17 100644 --- a/docs/2.2.0/lms-connection/index.html +++ b/docs/2.2.0/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with an LMS | Richie - - + +
    -
    Version: 2.2.0

    Connecting Richie with an LMS

    richie can be connected to one or more Learning Management Systems (LMS) like OpenEdx, Moodle +

    Version: 2.2.0

    Connecting Richie with an LMS

    richie can be connected to one or more Learning Management Systems (LMS) like OpenEdx, Moodle or Canvas for a seamless experience between browsing the course catalog on richie and following the course itself on the LMS.

    In order to connect richie with a LMS, there is an API bridge to synchronize course information and enrollments.

    API bridge

    The APIHandler utility acts as a proxy that routes queries to the correct LMS backend API, @@ -43,7 +43,7 @@ on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. If you want to use SSL later, just use make run-ssl to run OpenEdx and Richie apps. Of course, you can still run apps without ssl by using make run.

    - - + + \ No newline at end of file diff --git a/docs/2.2.0/native-installation/index.html b/docs/2.2.0/native-installation/index.html index 5185a45f6d..0b7642c98a 100644 --- a/docs/2.2.0/native-installation/index.html +++ b/docs/2.2.0/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.2.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.2.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.20.0/accessibility-testing/index.html b/docs/2.20.0/accessibility-testing/index.html index 84a8d09a8e..d961a87325 100644 --- a/docs/2.20.0/accessibility-testing/index.html +++ b/docs/2.20.0/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.20.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems on any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two ways to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.20.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems on any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two ways to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.20.0/api/course-run-synchronization-api/index.html b/docs/2.20.0/api/course-run-synchronization-api/index.html index d18b291d6b..c21bafd297 100644 --- a/docs/2.20.0/api/course-run-synchronization-api/index.html +++ b/docs/2.20.0/api/course-run-synchronization-api/index.html @@ -4,12 +4,12 @@ Course run synchronization API | Richie - - + +
    -
    Version: 2.20.0

    Course run synchronization API

    API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

    Synchronization endpoint [/api/1.0/course-runs-sync]

    This documentation describes version "1.0" of this API endpoint.

    Synchronize a course run [POST]

    It takes a JSON object containing the course run details:

    • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - +
      Version: 2.20.0

      Course run synchronization API

      API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

      Synchronization endpoint [/api/1.0/course-runs-sync]

      This documentation describes version "1.0" of this API endpoint.

      Synchronize a course run [POST]

      It takes a JSON object containing the course run details:

      • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - url of the course syllabus on the LMS from which a unique course identifier can be extracted
      • start: 2018-02-01T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course starts
      • end: 2018-02-28T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course ends
      • enrollment_start: 2018-01-01T06:00:00Z (string, optional) - ISO 8601 date, when enrollment @@ -19,7 +19,7 @@ containing the digest of the utf-8 encoded json representation of the submitted data for the given secret key and SHA256 digest algorithm (see [synchronizing-course-runs] for an example).
    • Body

        {
      "resource_link": "https://lms.example.com/courses/course-v1:001+001+001/info",
      "start": "2021-02-01T00:00:00Z",
      "end": "2021-02-31T23:59:59Z",
      "enrollment_start": "2021-01-01T00:00:00Z",
      "enrollment_end": "2021-01-31T23:59:59Z",
      "languages": ["en", "fr"]
      }
  • Response 200 (application/json)

    • Body

        {
      "success": True
      }
  • - - + + \ No newline at end of file diff --git a/docs/2.20.0/building-the-frontend/index.html b/docs/2.20.0/building-the-frontend/index.html index e81705f89c..f2715fc7a3 100644 --- a/docs/2.20.0/building-the-frontend/index.html +++ b/docs/2.20.0/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.20.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.20.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.20.0/contributing-guide/index.html b/docs/2.20.0/contributing-guide/index.html index 5ac26ee9ad..1d003aa42c 100644 --- a/docs/2.20.0/contributing-guide/index.html +++ b/docs/2.20.0/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.20.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.20.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.20.0/cookiecutter/index.html b/docs/2.20.0/cookiecutter/index.html index 7d60ddfd2f..edf6eef629 100644 --- a/docs/2.20.0/cookiecutter/index.html +++ b/docs/2.20.0/cookiecutter/index.html @@ -4,12 +4,12 @@ Start your own site | Richie - - + +
    -
    Version: 2.20.0

    Start your own site

    We use Cookiecutter to help you +

    Version: 2.20.0

    Start your own site

    We use Cookiecutter to help you set up a production-ready learning portal website based on Richie in seconds.

    Run Cookiecutter

    There are 2 options to run Cookiecutter:

    While you think of it, navigate to the directory in which you want to create your site factory:

    cd /path/to/your/code/directory

    If you chose to install Cookiecutter, you can now run it against our @@ -30,7 +30,7 @@ project's scaffolding but, don't worry, it will respect all the sites you will have created in the sites directory:

    cookiecutter --overwrite-if-exists gh:openfun/richie --directory=cookiecutter

    Help us improve this project

    After starting your project, please submit an issue let us know how it went and what other features we should add to make it better.

    - - + + \ No newline at end of file diff --git a/docs/2.20.0/css-guidelines/index.html b/docs/2.20.0/css-guidelines/index.html index 0721b59706..f4a2fadc86 100644 --- a/docs/2.20.0/css-guidelines/index.html +++ b/docs/2.20.0/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.20.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.20.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.20.0/discover/index.html b/docs/2.20.0/discover/index.html index f3f00f8604..6bae0ca417 100644 --- a/docs/2.20.0/discover/index.html +++ b/docs/2.20.0/discover/index.html @@ -4,12 +4,12 @@ Discover Richie | Richie - - + +
    -
    Version: 2.20.0

    Discover Richie

    Learning Management Systems (LMS) are great tools for hosting and playing interactive online +

    Version: 2.20.0

    Discover Richie

    Learning Management Systems (LMS) are great tools for hosting and playing interactive online courses and MOOCs.

    However, if you need to build a complete website with flexible content to aggregate your courses, in several languages and from different sources, you will soon need a CMS.

    At "France Université Numérique", we wanted to build an OpenSource portal with Python and Django. That's why we built Richie on top of DjangoCMS, one of @@ -21,7 +21,7 @@ OpenEdX Docker.

    Two users are available for testing:

    • admin: admin@example.com/admin
    • student: edx@example.com/edx

    The database is regularly flushed.

    Start your own site

    The next step after discovering Richie on the demo is to start your own project. We provide a production-ready template based on Cookiecutter that allows you to generate your project in seconds.

    Follow the guide on starting your own Richie site with Cookiecutter.

    Once you created a new site, you will be able to fully customize it:

    - - + + \ No newline at end of file diff --git a/docs/2.20.0/displaying-connection-status/index.html b/docs/2.20.0/displaying-connection-status/index.html index 0a66caa38f..5587e8d040 100644 --- a/docs/2.20.0/displaying-connection-status/index.html +++ b/docs/2.20.0/displaying-connection-status/index.html @@ -4,12 +4,12 @@ Displaying OpenEdX connection status in Richie | Richie - - + +
    -
    Version: 2.20.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status +

    Version: 2.20.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status and display profile information for the logged-in user in Richie.

    In Richie, the only users that are actually authenticated on the DjangoCMS instance producing the site, are editors and staff users. Your instructors and learners will not have user accounts on Richie, but authentication is delegated to a remote service that can be OpenEdX, KeyCloack, or @@ -26,7 +26,7 @@ set for the above authentication delegation BASE_URL setting.

    If you need to bind user data into a url, wrap the property between brackets. For example, the link configured above for the profile page {base_url:s}/u/(username) would point to https://lms.example.com/u/johndoe for a user carrying the username johndoe.

    - - + + \ No newline at end of file diff --git a/docs/2.20.0/django-react-interop/index.html b/docs/2.20.0/django-react-interop/index.html index 9a7f3f0579..52ecdf7ace 100644 --- a/docs/2.20.0/django-react-interop/index.html +++ b/docs/2.20.0/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.20.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.20.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.20.0/docker-development/index.html b/docs/2.20.0/docker-development/index.html index b4e6602449..d8782c385a 100644 --- a/docs/2.20.0/docker-development/index.html +++ b/docs/2.20.0/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.20.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.20.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.20.0/filters-customization/index.html b/docs/2.20.0/filters-customization/index.html index 80bc029bff..38d640ae7d 100644 --- a/docs/2.20.0/filters-customization/index.html +++ b/docs/2.20.0/filters-customization/index.html @@ -4,12 +4,12 @@ Customizing search filters | Richie - - + +
    -
    Version: 2.20.0

    Customizing search filters

    You may want to customize the filters on the left side bar of the search page.

    Richie makes it easy to choose which filters you want to display among the existing filters +

    Version: 2.20.0

    Customizing search filters

    You may want to customize the filters on the left side bar of the search page.

    Richie makes it easy to choose which filters you want to display among the existing filters and in which order. You can also configure the existing filters to change their title or the way they behave. Lastly, you can completely override a filter or create your own custom filter from scratch.

    Filters configuration

    Filters must first be defined in the FILTERS_CONFIGURATION setting. It is a dictionary defining @@ -48,7 +48,7 @@ trivial, was given much care and includes many comments in an attempt to help writing new custom filters. Of course, don't hesitate to ask for help by opening an issue!

    - - + + \ No newline at end of file diff --git a/docs/2.20.0/frontend-overrides/index.html b/docs/2.20.0/frontend-overrides/index.html index 2435aa5fea..883bb4c98f 100644 --- a/docs/2.20.0/frontend-overrides/index.html +++ b/docs/2.20.0/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.20.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.20.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.20.0/installation/index.html b/docs/2.20.0/installation/index.html index cd45b035d0..daf55be14a 100644 --- a/docs/2.20.0/installation/index.html +++ b/docs/2.20.0/installation/index.html @@ -4,12 +4,12 @@ Installing Richie for development | Richie - - + +
    -
    Version: 2.20.0

    Installing Richie for development

    Richie is a container-native application but can also be installed +

    Version: 2.20.0

    Installing Richie for development

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of:

    • 3 running services:

      • database: postgresql or mysql at your preference,
      • elasticsearch: the search engine,
      • app: the actual DjangoCMS project with all our application code.
    • 2 containers for building purposes:

    - - + + \ No newline at end of file diff --git a/docs/2.20.0/internationalization/index.html b/docs/2.20.0/internationalization/index.html index 8b3c09729e..9f9b2e39f7 100644 --- a/docs/2.20.0/internationalization/index.html +++ b/docs/2.20.0/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.20.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.20.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.20.0/joanie-connection/index.html b/docs/2.20.0/joanie-connection/index.html index 929666c80d..56ae2b8115 100644 --- a/docs/2.20.0/joanie-connection/index.html +++ b/docs/2.20.0/joanie-connection/index.html @@ -4,12 +4,12 @@ Joanie Connection | Richie - - + +
    -
    Version: 2.20.0

    Joanie Connection

    Joanie delivers an API able to manage course +

    Version: 2.20.0

    Joanie Connection

    Joanie delivers an API able to manage course enrollment/subscription, payment and certificates delivery. Richie can be configured to display course runs and micro-credentials managed through Joanie.

    In fact, Richie treats Joanie almost like a LMS backend that's why settings are similars.

    Configuring Joanie

    All settings related to Joanie have to be declared in the JOANIE_BACKEND dictionnary @@ -40,7 +40,7 @@ authentication server is using djangorestframework-simplejwt to generate the access token, its lifetime is 5 minutes by default.

    Technical support

    If you encounter an issue with this documentation, please open an issue on our repository.

    - - + + \ No newline at end of file diff --git a/docs/2.20.0/lms-backends/index.html b/docs/2.20.0/lms-backends/index.html index 96e444549f..2bf9aaca4c 100644 --- a/docs/2.20.0/lms-backends/index.html +++ b/docs/2.20.0/lms-backends/index.html @@ -4,12 +4,12 @@ Configuring LMS Backends | Richie - - + +
    -
    Version: 2.20.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless +

    Version: 2.20.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless experience between browsing the course catalog on Richie, enrolling to a course and following the course itself on the LMS.

    It is possible to do the same with Moodle or any other LMS exposing an enrollment API, at the cost of writing a custom LMS handler backend.

    Prerequisites

    This connection requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that @@ -36,7 +36,7 @@ value other then manual.

    • Type: enum(string)
    • Required: No
    • Value: for example ["languages"]

    Technical support

    If you encounter an issue with this documentation or the backends included in Richie, please open an issue on our repository.

    If you need a custom backend, you can submit a PR or open an issue and we will consider adding it.

    - - + + \ No newline at end of file diff --git a/docs/2.20.0/lms-connection/index.html b/docs/2.20.0/lms-connection/index.html index 50ed606a2e..0b4252072a 100644 --- a/docs/2.20.0/lms-connection/index.html +++ b/docs/2.20.0/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with one or more LMS | Richie - - + +
    -
    Version: 2.20.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated +

    Version: 2.20.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated seamless experience.

    As of today, each approach has been implemented for OpenEdX but the same could be done for other LMSes like Moodle, at the cost of minor adaptations.

    1. Displaying connection status

    OpenEdX can be configured to allow CORS requests. Doing so allows Richie to retrieve a user's connection status from OpenEdx and display the user's profile information directly on the Richie @@ -31,7 +31,7 @@ docker-compose.

    If you want to activate seamless enrollment locally for development, you will need to set up TLS domains for both Richie and OpenEdX. To do this, head over to our guide on setting-up TLS connections for Richie and OpenEdX.

    - - + + \ No newline at end of file diff --git a/docs/2.20.0/native-installation/index.html b/docs/2.20.0/native-installation/index.html index fd0e3cd9fb..7c2bf1b5b2 100644 --- a/docs/2.20.0/native-installation/index.html +++ b/docs/2.20.0/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.20.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.20.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native installation instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.20.0/synchronizing-course-runs/index.html b/docs/2.20.0/synchronizing-course-runs/index.html index 3a5f72010c..a8c7d903d2 100644 --- a/docs/2.20.0/synchronizing-course-runs/index.html +++ b/docs/2.20.0/synchronizing-course-runs/index.html @@ -4,12 +4,12 @@ Synchronizing course runs between Richie and OpenEdX | Richie - - + +
    -
    Version: 2.20.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the +

    Version: 2.20.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the RICHIE_COURSE_RUN_SYNC_SECRETS setting with one or more secrets:

    RICHIE_COURSE_RUN_SYNC_SECRETS = ["SharedSecret", "OtherSharedSecret"]

    This setting collects several secrets in order to allow rotating them without any downtime. Any of the secrets listed in this setting can be used to sign your queries.

    Your secret should be shared with the LMS or distant system that needs to synchronize its course runs with the Richie instance. Richie will try the declared secrets one by one until it finds @@ -29,7 +29,7 @@ course run is modified and published.

    Or you can use the Richie Open edX Synchronization which is based on the following code sample and also includes the enrollment count.

    Given a COURSE_HOOK setting defined as follows in your OpenEdX instance:

    COURSE_HOOK = {
    "secret": "SharedSecret",
    "url": "https://richie.example.com/api/v1.0/course-runs-sync/",
    }

    The code for the synchronization function in OpenEdX could look like this:

    import hashlib
    import hmac
    import json

    from django.conf import settings

    from microsite_configuration import microsite
    import requests
    from xmodule.modulestore.django import modulestore


    def update_course(course_key, *args, **kwargs):
    """Synchronize an OpenEdX course, identified by its course key, with a Richie instance."""
    course = modulestore().get_course(course_key)
    edxapp_domain = microsite.get_value("site_domain", settings.LMS_BASE)

    data = {
    "resource_link": "https://{:s}/courses/{!s}/info".format(
    edxapp_domain, course_key
    ),
    "start": course.start and course.start.isoformat(),
    "end": course.end and course.end.isoformat(),
    "enrollment_start": course.enrollment_start and course.enrollment_start.isoformat(),
    "enrollment_end": course.enrollment_end and course.enrollment_end.isoformat(),
    "languages": [course.language or settings.LANGUAGE_CODE],
    }

    signature = hmac.new(
    setting.COURSE_HOOK["secret"].encode("utf-8"),
    msg=json.dumps(data).encode("utf-8"),
    digestmod=hashlib.sha256,
    ).hexdigest()

    response = requests.post(
    setting.COURSE_HOOK["url"],
    json=data,
    headers={"Authorization": "SIG-HMAC-SHA256 {:s}".format(signature)},
    )

    Thanks to the signal emitted in OpenEdX, this function can then be triggered each time a course is modified and published:

    from django.dispatch import receiver
    from xmodule.modulestore.django import SignalHandler


    @receiver(SignalHandler.course_published, dispatch_uid='update_course_on_publish')
    def update_course_on_publish(sender, course_key, **kwargs):
    update_course(course_key)
    - - + + \ No newline at end of file diff --git a/docs/2.20.0/tls-connection/index.html b/docs/2.20.0/tls-connection/index.html index ebd929da99..e319093fca 100644 --- a/docs/2.20.0/tls-connection/index.html +++ b/docs/2.20.0/tls-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie and OpenEdX over TLS for development | Richie - - + +
    -
    Version: 2.20.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 +

    Version: 2.20.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 and uses a fake LMS backend (base.BaseLMSBackend) as you can see if you check the RICHIE_LMS_BACKENDS setting in env.d/development.

    This base backend uses session storage to fake enrollments to course runs.

    If you want to test real enrollments to an OpenEdX instance hosted on an external domain, OpenEdX will need to generate a CORS CSRF Cookie. This cookie is flagged as secure, which implies that @@ -32,7 +32,7 @@ to share cookies with the OpenEdx application to allow CORS CSRF Protected XHR requests.

    4. Start Richie and OpenEdx over SSL

    Now, the OpenEdx application should respond on https://edx.local.dev:8073, and Richie on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. The next time you want to use SSL, you can run the following command on both the Richie and OpenEdX projects:

    $ make run-ssl

    Of course, you can still run apps without ssl by using:

    $ make run
    - - + + \ No newline at end of file diff --git a/docs/2.20.0/web-analytics/index.html b/docs/2.20.0/web-analytics/index.html index 28615aa729..5bbc6d3bf5 100644 --- a/docs/2.20.0/web-analytics/index.html +++ b/docs/2.20.0/web-analytics/index.html @@ -4,19 +4,19 @@ Add web analytics to your site | Richie - - + +
    -
    Version: 2.20.0

    Add web analytics to your site

    Richie has native support to Google Analytics and Google Tag Manager Web Analytics solutions. +

    Version: 2.20.0

    Add web analytics to your site

    Richie has native support to Google Analytics and Google Tag Manager Web Analytics solutions. The purpose of this file is to explain how you can enable one of the supported Web Analytics providers and how you can extend Richie with an alternative solution.

    Google Analytics

    Next, it is described how you can configure the Google Analytics on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Analytics tracking id code.

    The current Google Analytics implementation also includes custom dimensions. Those dimensions permit you to create further analyses on Google Analytics or even use them to create custom reports. Custom dimensions with a value as example:

    • Organizations codes - UNIV_LISBON | UNIV_PORTO
    • Course code - COURSE_XPTO
    • Course runs titles - Summer edition | Winter edition
    • Course runs resource links - http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/info
    • Page title - Introduction to Programming

    Google Tag Manager

    Next, it is described how you can configure the Google Tag Manager on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Tag Manager tracking id code.
    • Add the WEB_ANALYTICS_PROVIDER setting with the google_tag_manager value.

    The current Google Tag Manager implementation also defines a custom dimensions like the Google Analytics.

    Location of the web analytics javascript

    Use the WEB_ANALYTICS_LOCATION settings to decide where do you want to put the Javascript code. Use head (default value), to put the Javascript on HTML header, or footer, to put the Javascript code to the bottom of the body.

    Add a new Web Analytics solution

    In this section it's described how you can add support to a different Web Analytics solution.

    • override the richie/web_analytics.html template
    • define the WEB_ANALYTICS_ID setting with your tracking identification
    • define the WEB_ANALYTICS_PROVIDER setting with a value that represents your solution, eg. my-custom-web-analytics-software
    • optionally change WEB_ANALYTICS_LOCATION setting with head (default) or footer value
    • Example of a richie/web_analytics.html file customization that prints to the browser console log the dimension keys and values:
    <script type="text/javascript">
    {% for dimension_key, dimension_value_list in WEB_ANALYTICS.DIMENSIONS.items %}
    console.log("dimension: index '{{forloop.counter}}' with key '{{ dimension_key }}' with value '{{ dimension_value_list|join:" | " }}'");
    {% endfor %}
    </script>

    Output:

    dimension: index '1' with key 'organizations_codes' with value 'COMPATIBLE-EVEN-KEELED-UTILIZATION-19 | FOCUSED-NEXT-GENERATION-FUNCTIONALITIES-22 | UNIVERSAL-MODULAR-LOCAL-AREA-NETWORK-23'
    dimension: index '2' with key 'course_code' with value '00017'
    dimension: index '3' with key 'course_runs_titles' with value 'Run 0'
    dimension: index '4' with key 'course_runs_resource_links' with value ''
    dimension: index '5' with key 'page_title' with value 'Business-focused zero-defect application'

    But you can also contribute to Richie by creating a pull request to add support for a different web analytics solution. In this last case, you have to edit directly the richie/web_analytics.html template.

    Example of an override of the richie/web_analytics.html file:

    {% block web_analytics %}
    {% if WEB_ANALYTICS_ID %}
    {% if WEB_ANALYTICS_PROVIDER == "my_custom_web_analytics_software" %}
    <script type="text/javascript" src="{% static 'myapp/js/custom_web_analytics_software.js' %}">
    <script type="text/javascript">
    // javascript code that startups the custom web analytics software
    </script>
    {% endif %}
    {% endif %}
    {% endblock web_analytics %}

    The web analytics dimensions are being added to the django context using the WEB_ANALYTICS.DIMENSIONS dictionary. Because each dimension value could have multiple values, then each dictionary value is a list. Web analytics dimensions dictionary keys:

    • organizations_codes
    • course_code
    • course_runs_titles
    • course_runs_resource_links
    • page_title

    Example, if you only need the organization codes on your custom richie/web_analytics.html file:

    <script type="text/javascript">
    console.log("organization codes: '{{ WEB_ANALYTICS.DIMENSIONS.organizations_codes |join:" | " }}");
    </script>

    The frontend code also sends events to the web analytics provider. Richie sends events when the user is enrolled on a course run. To support different providers, you need to create a similar file of src/frontend/js/utils/api/web-analytics/google_analytics.ts and change the src/frontend/js/utils/api/web-analytics/index.ts file to include that newer provider.

    - - + + \ No newline at end of file diff --git a/docs/2.20.1/accessibility-testing/index.html b/docs/2.20.1/accessibility-testing/index.html index ad9ca3baa3..a8820fc1d5 100644 --- a/docs/2.20.1/accessibility-testing/index.html +++ b/docs/2.20.1/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.20.1

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems on any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two ways to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.20.1

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems on any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two ways to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.20.1/api/course-run-synchronization-api/index.html b/docs/2.20.1/api/course-run-synchronization-api/index.html index be0ae49d8c..07104c5e6f 100644 --- a/docs/2.20.1/api/course-run-synchronization-api/index.html +++ b/docs/2.20.1/api/course-run-synchronization-api/index.html @@ -4,12 +4,12 @@ Course run synchronization API | Richie - - + +
    -
    Version: 2.20.1

    Course run synchronization API

    API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

    Synchronization endpoint [/api/1.0/course-runs-sync]

    This documentation describes version "1.0" of this API endpoint.

    Synchronize a course run [POST]

    It takes a JSON object containing the course run details:

    • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - +
      Version: 2.20.1

      Course run synchronization API

      API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

      Synchronization endpoint [/api/1.0/course-runs-sync]

      This documentation describes version "1.0" of this API endpoint.

      Synchronize a course run [POST]

      It takes a JSON object containing the course run details:

      • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - url of the course syllabus on the LMS from which a unique course identifier can be extracted
      • start: 2018-02-01T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course starts
      • end: 2018-02-28T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course ends
      • enrollment_start: 2018-01-01T06:00:00Z (string, optional) - ISO 8601 date, when enrollment @@ -19,7 +19,7 @@ containing the digest of the utf-8 encoded json representation of the submitted data for the given secret key and SHA256 digest algorithm (see [synchronizing-course-runs] for an example).
    • Body

        {
      "resource_link": "https://lms.example.com/courses/course-v1:001+001+001/info",
      "start": "2021-02-01T00:00:00Z",
      "end": "2021-02-31T23:59:59Z",
      "enrollment_start": "2021-01-01T00:00:00Z",
      "enrollment_end": "2021-01-31T23:59:59Z",
      "languages": ["en", "fr"]
      }
  • Response 200 (application/json)

    • Body

        {
      "success": True
      }
  • - - + + \ No newline at end of file diff --git a/docs/2.20.1/building-the-frontend/index.html b/docs/2.20.1/building-the-frontend/index.html index a12edd96b0..0d6f7367a7 100644 --- a/docs/2.20.1/building-the-frontend/index.html +++ b/docs/2.20.1/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.20.1

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.20.1

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.20.1/contributing-guide/index.html b/docs/2.20.1/contributing-guide/index.html index ef5d41a89f..3c28ca72e8 100644 --- a/docs/2.20.1/contributing-guide/index.html +++ b/docs/2.20.1/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.20.1

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.20.1

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.20.1/cookiecutter/index.html b/docs/2.20.1/cookiecutter/index.html index 7cfdbc1cae..34167b2430 100644 --- a/docs/2.20.1/cookiecutter/index.html +++ b/docs/2.20.1/cookiecutter/index.html @@ -4,12 +4,12 @@ Start your own site | Richie - - + +
    -
    Version: 2.20.1

    Start your own site

    We use Cookiecutter to help you +

    Version: 2.20.1

    Start your own site

    We use Cookiecutter to help you set up a production-ready learning portal website based on Richie in seconds.

    Run Cookiecutter

    There are 2 options to run Cookiecutter:

    While you think of it, navigate to the directory in which you want to create your site factory:

    cd /path/to/your/code/directory

    If you chose to install Cookiecutter, you can now run it against our @@ -30,7 +30,7 @@ project's scaffolding but, don't worry, it will respect all the sites you will have created in the sites directory:

    cookiecutter --overwrite-if-exists gh:openfun/richie --directory=cookiecutter

    Help us improve this project

    After starting your project, please submit an issue let us know how it went and what other features we should add to make it better.

    - - + + \ No newline at end of file diff --git a/docs/2.20.1/css-guidelines/index.html b/docs/2.20.1/css-guidelines/index.html index ad87041b87..3770c1adb1 100644 --- a/docs/2.20.1/css-guidelines/index.html +++ b/docs/2.20.1/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.20.1

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.20.1

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.20.1/discover/index.html b/docs/2.20.1/discover/index.html index 1c613c5d8e..e2aa1d946b 100644 --- a/docs/2.20.1/discover/index.html +++ b/docs/2.20.1/discover/index.html @@ -4,12 +4,12 @@ Discover Richie | Richie - - + +
    -
    Version: 2.20.1

    Discover Richie

    Learning Management Systems (LMS) are great tools for hosting and playing interactive online +

    Version: 2.20.1

    Discover Richie

    Learning Management Systems (LMS) are great tools for hosting and playing interactive online courses and MOOCs.

    However, if you need to build a complete website with flexible content to aggregate your courses, in several languages and from different sources, you will soon need a CMS.

    At "France Université Numérique", we wanted to build an OpenSource portal with Python and Django. That's why we built Richie on top of DjangoCMS, one of @@ -21,7 +21,7 @@ OpenEdX Docker.

    Two users are available for testing:

    • admin: admin@example.com/admin
    • student: edx@example.com/edx

    The database is regularly flushed.

    Start your own site

    The next step after discovering Richie on the demo is to start your own project. We provide a production-ready template based on Cookiecutter that allows you to generate your project in seconds.

    Follow the guide on starting your own Richie site with Cookiecutter.

    Once you created a new site, you will be able to fully customize it:

    - - + + \ No newline at end of file diff --git a/docs/2.20.1/displaying-connection-status/index.html b/docs/2.20.1/displaying-connection-status/index.html index 49f2b4994f..ab079874a7 100644 --- a/docs/2.20.1/displaying-connection-status/index.html +++ b/docs/2.20.1/displaying-connection-status/index.html @@ -4,12 +4,12 @@ Displaying OpenEdX connection status in Richie | Richie - - + +
    -
    Version: 2.20.1

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status +

    Version: 2.20.1

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status and display profile information for the logged-in user in Richie.

    In Richie, the only users that are actually authenticated on the DjangoCMS instance producing the site, are editors and staff users. Your instructors and learners will not have user accounts on Richie, but authentication is delegated to a remote service that can be OpenEdX, KeyCloack, or @@ -26,7 +26,7 @@ set for the above authentication delegation BASE_URL setting.

    If you need to bind user data into a url, wrap the property between brackets. For example, the link configured above for the profile page {base_url:s}/u/(username) would point to https://lms.example.com/u/johndoe for a user carrying the username johndoe.

    - - + + \ No newline at end of file diff --git a/docs/2.20.1/django-react-interop/index.html b/docs/2.20.1/django-react-interop/index.html index 8c1e7930e5..47f0568c99 100644 --- a/docs/2.20.1/django-react-interop/index.html +++ b/docs/2.20.1/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.20.1

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.20.1

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.20.1/docker-development/index.html b/docs/2.20.1/docker-development/index.html index 470c4e3ed5..3cac1795cb 100644 --- a/docs/2.20.1/docker-development/index.html +++ b/docs/2.20.1/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.20.1

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.20.1

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.20.1/filters-customization/index.html b/docs/2.20.1/filters-customization/index.html index c0e026e6bc..98198cd5af 100644 --- a/docs/2.20.1/filters-customization/index.html +++ b/docs/2.20.1/filters-customization/index.html @@ -4,12 +4,12 @@ Customizing search filters | Richie - - + +
    -
    Version: 2.20.1

    Customizing search filters

    You may want to customize the filters on the left side bar of the search page.

    Richie makes it easy to choose which filters you want to display among the existing filters +

    Version: 2.20.1

    Customizing search filters

    You may want to customize the filters on the left side bar of the search page.

    Richie makes it easy to choose which filters you want to display among the existing filters and in which order. You can also configure the existing filters to change their title or the way they behave. Lastly, you can completely override a filter or create your own custom filter from scratch.

    Filters configuration

    Filters must first be defined in the FILTERS_CONFIGURATION setting. It is a dictionary defining @@ -48,7 +48,7 @@ trivial, was given much care and includes many comments in an attempt to help writing new custom filters. Of course, don't hesitate to ask for help by opening an issue!

    - - + + \ No newline at end of file diff --git a/docs/2.20.1/frontend-overrides/index.html b/docs/2.20.1/frontend-overrides/index.html index 30d10c3016..85c43be322 100644 --- a/docs/2.20.1/frontend-overrides/index.html +++ b/docs/2.20.1/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.20.1

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.20.1

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.20.1/installation/index.html b/docs/2.20.1/installation/index.html index 9fa1e40ade..4f76f2a9d7 100644 --- a/docs/2.20.1/installation/index.html +++ b/docs/2.20.1/installation/index.html @@ -4,12 +4,12 @@ Installing Richie for development | Richie - - + +
    -
    Version: 2.20.1

    Installing Richie for development

    Richie is a container-native application but can also be installed +

    Version: 2.20.1

    Installing Richie for development

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of:

    • 3 running services:

      • database: postgresql or mysql at your preference,
      • elasticsearch: the search engine,
      • app: the actual DjangoCMS project with all our application code.
    • 2 containers for building purposes:

    - - + + \ No newline at end of file diff --git a/docs/2.20.1/internationalization/index.html b/docs/2.20.1/internationalization/index.html index 67c5c35c6b..93a3afd88d 100644 --- a/docs/2.20.1/internationalization/index.html +++ b/docs/2.20.1/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.20.1

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.20.1

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.20.1/joanie-connection/index.html b/docs/2.20.1/joanie-connection/index.html index aef6075ee8..f1430ed533 100644 --- a/docs/2.20.1/joanie-connection/index.html +++ b/docs/2.20.1/joanie-connection/index.html @@ -4,12 +4,12 @@ Joanie Connection | Richie - - + +
    -
    Version: 2.20.1

    Joanie Connection

    Joanie delivers an API able to manage course +

    Version: 2.20.1

    Joanie Connection

    Joanie delivers an API able to manage course enrollment/subscription, payment and certificates delivery. Richie can be configured to display course runs and micro-credentials managed through Joanie.

    In fact, Richie treats Joanie almost like a LMS backend that's why settings are similars.

    Configuring Joanie

    All settings related to Joanie have to be declared in the JOANIE_BACKEND dictionnary @@ -40,7 +40,7 @@ authentication server is using djangorestframework-simplejwt to generate the access token, its lifetime is 5 minutes by default.

    Technical support

    If you encounter an issue with this documentation, please open an issue on our repository.

    - - + + \ No newline at end of file diff --git a/docs/2.20.1/lms-backends/index.html b/docs/2.20.1/lms-backends/index.html index 1ec8eb4cdc..2181290a17 100644 --- a/docs/2.20.1/lms-backends/index.html +++ b/docs/2.20.1/lms-backends/index.html @@ -4,12 +4,12 @@ Configuring LMS Backends | Richie - - + +
    -
    Version: 2.20.1

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless +

    Version: 2.20.1

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless experience between browsing the course catalog on Richie, enrolling to a course and following the course itself on the LMS.

    It is possible to do the same with Moodle or any other LMS exposing an enrollment API, at the cost of writing a custom LMS handler backend.

    Prerequisites

    This connection requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that @@ -36,7 +36,7 @@ value other then manual.

    • Type: enum(string)
    • Required: No
    • Value: for example ["languages"]

    Technical support

    If you encounter an issue with this documentation or the backends included in Richie, please open an issue on our repository.

    If you need a custom backend, you can submit a PR or open an issue and we will consider adding it.

    - - + + \ No newline at end of file diff --git a/docs/2.20.1/lms-connection/index.html b/docs/2.20.1/lms-connection/index.html index b7d1345485..90835137e0 100644 --- a/docs/2.20.1/lms-connection/index.html +++ b/docs/2.20.1/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with one or more LMS | Richie - - + +
    -
    Version: 2.20.1

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated +

    Version: 2.20.1

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated seamless experience.

    As of today, each approach has been implemented for OpenEdX but the same could be done for other LMSes like Moodle, at the cost of minor adaptations.

    1. Displaying connection status

    OpenEdX can be configured to allow CORS requests. Doing so allows Richie to retrieve a user's connection status from OpenEdx and display the user's profile information directly on the Richie @@ -31,7 +31,7 @@ docker-compose.

    If you want to activate seamless enrollment locally for development, you will need to set up TLS domains for both Richie and OpenEdX. To do this, head over to our guide on setting-up TLS connections for Richie and OpenEdX.

    - - + + \ No newline at end of file diff --git a/docs/2.20.1/native-installation/index.html b/docs/2.20.1/native-installation/index.html index 2a03106ce7..13a0d69a97 100644 --- a/docs/2.20.1/native-installation/index.html +++ b/docs/2.20.1/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.20.1

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.20.1

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native installation instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.20.1/synchronizing-course-runs/index.html b/docs/2.20.1/synchronizing-course-runs/index.html index 48b8e99c6c..396b0d03a6 100644 --- a/docs/2.20.1/synchronizing-course-runs/index.html +++ b/docs/2.20.1/synchronizing-course-runs/index.html @@ -4,12 +4,12 @@ Synchronizing course runs between Richie and OpenEdX | Richie - - + +
    -
    Version: 2.20.1

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the +

    Version: 2.20.1

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the RICHIE_COURSE_RUN_SYNC_SECRETS setting with one or more secrets:

    RICHIE_COURSE_RUN_SYNC_SECRETS = ["SharedSecret", "OtherSharedSecret"]

    This setting collects several secrets in order to allow rotating them without any downtime. Any of the secrets listed in this setting can be used to sign your queries.

    Your secret should be shared with the LMS or distant system that needs to synchronize its course runs with the Richie instance. Richie will try the declared secrets one by one until it finds @@ -29,7 +29,7 @@ course run is modified and published.

    Or you can use the Richie Open edX Synchronization which is based on the following code sample and also includes the enrollment count.

    Given a COURSE_HOOK setting defined as follows in your OpenEdX instance:

    COURSE_HOOK = {
    "secret": "SharedSecret",
    "url": "https://richie.example.com/api/v1.0/course-runs-sync/",
    }

    The code for the synchronization function in OpenEdX could look like this:

    import hashlib
    import hmac
    import json

    from django.conf import settings

    from microsite_configuration import microsite
    import requests
    from xmodule.modulestore.django import modulestore


    def update_course(course_key, *args, **kwargs):
    """Synchronize an OpenEdX course, identified by its course key, with a Richie instance."""
    course = modulestore().get_course(course_key)
    edxapp_domain = microsite.get_value("site_domain", settings.LMS_BASE)

    data = {
    "resource_link": "https://{:s}/courses/{!s}/info".format(
    edxapp_domain, course_key
    ),
    "start": course.start and course.start.isoformat(),
    "end": course.end and course.end.isoformat(),
    "enrollment_start": course.enrollment_start and course.enrollment_start.isoformat(),
    "enrollment_end": course.enrollment_end and course.enrollment_end.isoformat(),
    "languages": [course.language or settings.LANGUAGE_CODE],
    }

    signature = hmac.new(
    setting.COURSE_HOOK["secret"].encode("utf-8"),
    msg=json.dumps(data).encode("utf-8"),
    digestmod=hashlib.sha256,
    ).hexdigest()

    response = requests.post(
    setting.COURSE_HOOK["url"],
    json=data,
    headers={"Authorization": "SIG-HMAC-SHA256 {:s}".format(signature)},
    )

    Thanks to the signal emitted in OpenEdX, this function can then be triggered each time a course is modified and published:

    from django.dispatch import receiver
    from xmodule.modulestore.django import SignalHandler


    @receiver(SignalHandler.course_published, dispatch_uid='update_course_on_publish')
    def update_course_on_publish(sender, course_key, **kwargs):
    update_course(course_key)
    - - + + \ No newline at end of file diff --git a/docs/2.20.1/tls-connection/index.html b/docs/2.20.1/tls-connection/index.html index 2bc055463c..168cb83e98 100644 --- a/docs/2.20.1/tls-connection/index.html +++ b/docs/2.20.1/tls-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie and OpenEdX over TLS for development | Richie - - + +
    -
    Version: 2.20.1

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 +

    Version: 2.20.1

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 and uses a fake LMS backend (base.BaseLMSBackend) as you can see if you check the RICHIE_LMS_BACKENDS setting in env.d/development.

    This base backend uses session storage to fake enrollments to course runs.

    If you want to test real enrollments to an OpenEdX instance hosted on an external domain, OpenEdX will need to generate a CORS CSRF Cookie. This cookie is flagged as secure, which implies that @@ -32,7 +32,7 @@ to share cookies with the OpenEdx application to allow CORS CSRF Protected XHR requests.

    4. Start Richie and OpenEdx over SSL

    Now, the OpenEdx application should respond on https://edx.local.dev:8073, and Richie on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. The next time you want to use SSL, you can run the following command on both the Richie and OpenEdX projects:

    $ make run-ssl

    Of course, you can still run apps without ssl by using:

    $ make run
    - - + + \ No newline at end of file diff --git a/docs/2.20.1/web-analytics/index.html b/docs/2.20.1/web-analytics/index.html index d34ce87392..a1ca1fba11 100644 --- a/docs/2.20.1/web-analytics/index.html +++ b/docs/2.20.1/web-analytics/index.html @@ -4,19 +4,19 @@ Add web analytics to your site | Richie - - + +
    -
    Version: 2.20.1

    Add web analytics to your site

    Richie has native support to Google Analytics and Google Tag Manager Web Analytics solutions. +

    Version: 2.20.1

    Add web analytics to your site

    Richie has native support to Google Analytics and Google Tag Manager Web Analytics solutions. The purpose of this file is to explain how you can enable one of the supported Web Analytics providers and how you can extend Richie with an alternative solution.

    Google Analytics

    Next, it is described how you can configure the Google Analytics on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Analytics tracking id code.

    The current Google Analytics implementation also includes custom dimensions. Those dimensions permit you to create further analyses on Google Analytics or even use them to create custom reports. Custom dimensions with a value as example:

    • Organizations codes - UNIV_LISBON | UNIV_PORTO
    • Course code - COURSE_XPTO
    • Course runs titles - Summer edition | Winter edition
    • Course runs resource links - http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/info
    • Page title - Introduction to Programming

    Google Tag Manager

    Next, it is described how you can configure the Google Tag Manager on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Tag Manager tracking id code.
    • Add the WEB_ANALYTICS_PROVIDER setting with the google_tag_manager value.

    The current Google Tag Manager implementation also defines a custom dimensions like the Google Analytics.

    Location of the web analytics javascript

    Use the WEB_ANALYTICS_LOCATION settings to decide where do you want to put the Javascript code. Use head (default value), to put the Javascript on HTML header, or footer, to put the Javascript code to the bottom of the body.

    Add a new Web Analytics solution

    In this section it's described how you can add support to a different Web Analytics solution.

    • override the richie/web_analytics.html template
    • define the WEB_ANALYTICS_ID setting with your tracking identification
    • define the WEB_ANALYTICS_PROVIDER setting with a value that represents your solution, eg. my-custom-web-analytics-software
    • optionally change WEB_ANALYTICS_LOCATION setting with head (default) or footer value
    • Example of a richie/web_analytics.html file customization that prints to the browser console log the dimension keys and values:
    <script type="text/javascript">
    {% for dimension_key, dimension_value_list in WEB_ANALYTICS.DIMENSIONS.items %}
    console.log("dimension: index '{{forloop.counter}}' with key '{{ dimension_key }}' with value '{{ dimension_value_list|join:" | " }}'");
    {% endfor %}
    </script>

    Output:

    dimension: index '1' with key 'organizations_codes' with value 'COMPATIBLE-EVEN-KEELED-UTILIZATION-19 | FOCUSED-NEXT-GENERATION-FUNCTIONALITIES-22 | UNIVERSAL-MODULAR-LOCAL-AREA-NETWORK-23'
    dimension: index '2' with key 'course_code' with value '00017'
    dimension: index '3' with key 'course_runs_titles' with value 'Run 0'
    dimension: index '4' with key 'course_runs_resource_links' with value ''
    dimension: index '5' with key 'page_title' with value 'Business-focused zero-defect application'

    But you can also contribute to Richie by creating a pull request to add support for a different web analytics solution. In this last case, you have to edit directly the richie/web_analytics.html template.

    Example of an override of the richie/web_analytics.html file:

    {% block web_analytics %}
    {% if WEB_ANALYTICS_ID %}
    {% if WEB_ANALYTICS_PROVIDER == "my_custom_web_analytics_software" %}
    <script type="text/javascript" src="{% static 'myapp/js/custom_web_analytics_software.js' %}">
    <script type="text/javascript">
    // javascript code that startups the custom web analytics software
    </script>
    {% endif %}
    {% endif %}
    {% endblock web_analytics %}

    The web analytics dimensions are being added to the django context using the WEB_ANALYTICS.DIMENSIONS dictionary. Because each dimension value could have multiple values, then each dictionary value is a list. Web analytics dimensions dictionary keys:

    • organizations_codes
    • course_code
    • course_runs_titles
    • course_runs_resource_links
    • page_title

    Example, if you only need the organization codes on your custom richie/web_analytics.html file:

    <script type="text/javascript">
    console.log("organization codes: '{{ WEB_ANALYTICS.DIMENSIONS.organizations_codes |join:" | " }}");
    </script>

    The frontend code also sends events to the web analytics provider. Richie sends events when the user is enrolled on a course run. To support different providers, you need to create a similar file of src/frontend/js/utils/api/web-analytics/google_analytics.ts and change the src/frontend/js/utils/api/web-analytics/index.ts file to include that newer provider.

    - - + + \ No newline at end of file diff --git a/docs/2.21.0/accessibility-testing/index.html b/docs/2.21.0/accessibility-testing/index.html index 762e767d81..2000a42444 100644 --- a/docs/2.21.0/accessibility-testing/index.html +++ b/docs/2.21.0/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.21.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems on any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two ways to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.21.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems on any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two ways to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.21.0/api/course-run-synchronization-api/index.html b/docs/2.21.0/api/course-run-synchronization-api/index.html index fa49a3460f..f55f1bc963 100644 --- a/docs/2.21.0/api/course-run-synchronization-api/index.html +++ b/docs/2.21.0/api/course-run-synchronization-api/index.html @@ -4,12 +4,12 @@ Course run synchronization API | Richie - - + +
    -
    Version: 2.21.0

    Course run synchronization API

    API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

    Synchronization endpoint [/api/1.0/course-runs-sync]

    This documentation describes version "1.0" of this API endpoint.

    Synchronize a course run [POST]

    It takes a JSON object containing the course run details:

    • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - +
      Version: 2.21.0

      Course run synchronization API

      API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

      Synchronization endpoint [/api/1.0/course-runs-sync]

      This documentation describes version "1.0" of this API endpoint.

      Synchronize a course run [POST]

      It takes a JSON object containing the course run details:

      • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - url of the course syllabus on the LMS from which a unique course identifier can be extracted
      • start: 2018-02-01T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course starts
      • end: 2018-02-28T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course ends
      • enrollment_start: 2018-01-01T06:00:00Z (string, optional) - ISO 8601 date, when enrollment @@ -19,7 +19,7 @@ containing the digest of the utf-8 encoded json representation of the submitted data for the given secret key and SHA256 digest algorithm (see [synchronizing-course-runs] for an example).
    • Body

        {
      "resource_link": "https://lms.example.com/courses/course-v1:001+001+001/info",
      "start": "2021-02-01T00:00:00Z",
      "end": "2021-02-31T23:59:59Z",
      "enrollment_start": "2021-01-01T00:00:00Z",
      "enrollment_end": "2021-01-31T23:59:59Z",
      "languages": ["en", "fr"]
      }
  • Response 200 (application/json)

    • Body

        {
      "success": True
      }
  • - - + + \ No newline at end of file diff --git a/docs/2.21.0/building-the-frontend/index.html b/docs/2.21.0/building-the-frontend/index.html index 543dffc1df..cb16113b16 100644 --- a/docs/2.21.0/building-the-frontend/index.html +++ b/docs/2.21.0/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.21.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.21.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.21.0/contributing-guide/index.html b/docs/2.21.0/contributing-guide/index.html index 942effa233..b8c51c9db3 100644 --- a/docs/2.21.0/contributing-guide/index.html +++ b/docs/2.21.0/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.21.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.21.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.21.0/cookiecutter/index.html b/docs/2.21.0/cookiecutter/index.html index 898c94a652..db31c6699f 100644 --- a/docs/2.21.0/cookiecutter/index.html +++ b/docs/2.21.0/cookiecutter/index.html @@ -4,12 +4,12 @@ Start your own site | Richie - - + +
    -
    Version: 2.21.0

    Start your own site

    We use Cookiecutter to help you +

    Version: 2.21.0

    Start your own site

    We use Cookiecutter to help you set up a production-ready learning portal website based on Richie in seconds.

    Run Cookiecutter

    There are 2 options to run Cookiecutter:

    While you think of it, navigate to the directory in which you want to create your site factory:

    cd /path/to/your/code/directory

    If you chose to install Cookiecutter, you can now run it against our @@ -30,7 +30,7 @@ project's scaffolding but, don't worry, it will respect all the sites you will have created in the sites directory:

    cookiecutter --overwrite-if-exists gh:openfun/richie --directory=cookiecutter

    Help us improve this project

    After starting your project, please submit an issue let us know how it went and what other features we should add to make it better.

    - - + + \ No newline at end of file diff --git a/docs/2.21.0/css-guidelines/index.html b/docs/2.21.0/css-guidelines/index.html index 1070cfac6e..44b166db83 100644 --- a/docs/2.21.0/css-guidelines/index.html +++ b/docs/2.21.0/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.21.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.21.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.21.0/discover/index.html b/docs/2.21.0/discover/index.html index 50a730ed50..78344bcd9f 100644 --- a/docs/2.21.0/discover/index.html +++ b/docs/2.21.0/discover/index.html @@ -4,12 +4,12 @@ Discover Richie | Richie - - + +
    -
    Version: 2.21.0

    Discover Richie

    Learning Management Systems (LMS) are great tools for hosting and playing interactive online +

    Version: 2.21.0

    Discover Richie

    Learning Management Systems (LMS) are great tools for hosting and playing interactive online courses and MOOCs.

    However, if you need to build a complete website with flexible content to aggregate your courses, in several languages and from different sources, you will soon need a CMS.

    At "France Université Numérique", we wanted to build an OpenSource portal with Python and Django. That's why we built Richie on top of DjangoCMS, one of @@ -21,7 +21,7 @@ OpenEdX Docker.

    Two users are available for testing:

    • admin: admin@example.com/admin
    • student: edx@example.com/edx

    The database is regularly flushed.

    Start your own site

    The next step after discovering Richie on the demo is to start your own project. We provide a production-ready template based on Cookiecutter that allows you to generate your project in seconds.

    Follow the guide on starting your own Richie site with Cookiecutter.

    Once you created a new site, you will be able to fully customize it:

    - - + + \ No newline at end of file diff --git a/docs/2.21.0/displaying-connection-status/index.html b/docs/2.21.0/displaying-connection-status/index.html index c842f0491c..86ab056ea7 100644 --- a/docs/2.21.0/displaying-connection-status/index.html +++ b/docs/2.21.0/displaying-connection-status/index.html @@ -4,12 +4,12 @@ Displaying OpenEdX connection status in Richie | Richie - - + +
    -
    Version: 2.21.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status +

    Version: 2.21.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status and display profile information for the logged-in user in Richie.

    In Richie, the only users that are actually authenticated on the DjangoCMS instance producing the site, are editors and staff users. Your instructors and learners will not have user accounts on Richie, but authentication is delegated to a remote service that can be OpenEdX, KeyCloack, or @@ -26,7 +26,7 @@ set for the above authentication delegation BASE_URL setting.

    If you need to bind user data into a url, wrap the property between brackets. For example, the link configured above for the profile page {base_url:s}/u/(username) would point to https://lms.example.com/u/johndoe for a user carrying the username johndoe.

    - - + + \ No newline at end of file diff --git a/docs/2.21.0/django-react-interop/index.html b/docs/2.21.0/django-react-interop/index.html index a4c39dcfaf..71e920014c 100644 --- a/docs/2.21.0/django-react-interop/index.html +++ b/docs/2.21.0/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.21.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.21.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.21.0/docker-development/index.html b/docs/2.21.0/docker-development/index.html index fc20bdb7c5..8f26c213d4 100644 --- a/docs/2.21.0/docker-development/index.html +++ b/docs/2.21.0/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.21.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.21.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.21.0/filters-customization/index.html b/docs/2.21.0/filters-customization/index.html index 250d828a48..027c9fc2dd 100644 --- a/docs/2.21.0/filters-customization/index.html +++ b/docs/2.21.0/filters-customization/index.html @@ -4,12 +4,12 @@ Customizing search filters | Richie - - + +
    -
    Version: 2.21.0

    Customizing search filters

    You may want to customize the filters on the left side bar of the search page.

    Richie makes it easy to choose which filters you want to display among the existing filters +

    Version: 2.21.0

    Customizing search filters

    You may want to customize the filters on the left side bar of the search page.

    Richie makes it easy to choose which filters you want to display among the existing filters and in which order. You can also configure the existing filters to change their title or the way they behave. Lastly, you can completely override a filter or create your own custom filter from scratch.

    Filters configuration

    Filters must first be defined in the FILTERS_CONFIGURATION setting. It is a dictionary defining @@ -48,7 +48,7 @@ trivial, was given much care and includes many comments in an attempt to help writing new custom filters. Of course, don't hesitate to ask for help by opening an issue!

    - - + + \ No newline at end of file diff --git a/docs/2.21.0/frontend-overrides/index.html b/docs/2.21.0/frontend-overrides/index.html index 0e8b7ee07b..a4b7563c97 100644 --- a/docs/2.21.0/frontend-overrides/index.html +++ b/docs/2.21.0/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.21.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.21.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.21.0/installation/index.html b/docs/2.21.0/installation/index.html index 20cbcb4cae..200f879ea6 100644 --- a/docs/2.21.0/installation/index.html +++ b/docs/2.21.0/installation/index.html @@ -4,12 +4,12 @@ Installing Richie for development | Richie - - + +
    -
    Version: 2.21.0

    Installing Richie for development

    Richie is a container-native application but can also be installed +

    Version: 2.21.0

    Installing Richie for development

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of:

    • 3 running services:

      • database: postgresql or mysql at your preference,
      • elasticsearch: the search engine,
      • app: the actual DjangoCMS project with all our application code.
    • 2 containers for building purposes:

    - - + + \ No newline at end of file diff --git a/docs/2.21.0/internationalization/index.html b/docs/2.21.0/internationalization/index.html index 3fb2934fcc..d1b32d1d12 100644 --- a/docs/2.21.0/internationalization/index.html +++ b/docs/2.21.0/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.21.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.21.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.21.0/joanie-connection/index.html b/docs/2.21.0/joanie-connection/index.html index 56c3c4b15f..c6fba5fa0c 100644 --- a/docs/2.21.0/joanie-connection/index.html +++ b/docs/2.21.0/joanie-connection/index.html @@ -4,12 +4,12 @@ Joanie Connection | Richie - - + +
    -
    Version: 2.21.0

    Joanie Connection

    Joanie delivers an API able to manage course +

    Version: 2.21.0

    Joanie Connection

    Joanie delivers an API able to manage course enrollment/subscription, payment and certificates delivery. Richie can be configured to display course runs and micro-credentials managed through Joanie.

    In fact, Richie treats Joanie almost like a LMS backend that's why settings are similars.

    Configuring Joanie

    All settings related to Joanie have to be declared in the JOANIE_BACKEND dictionnary @@ -40,7 +40,7 @@ authentication server is using djangorestframework-simplejwt to generate the access token, its lifetime is 5 minutes by default.

    Technical support

    If you encounter an issue with this documentation, please open an issue on our repository.

    - - + + \ No newline at end of file diff --git a/docs/2.21.0/lms-backends/index.html b/docs/2.21.0/lms-backends/index.html index cd72484524..de2ae4f83f 100644 --- a/docs/2.21.0/lms-backends/index.html +++ b/docs/2.21.0/lms-backends/index.html @@ -4,12 +4,12 @@ Configuring LMS Backends | Richie - - + +
    -
    Version: 2.21.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless +

    Version: 2.21.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless experience between browsing the course catalog on Richie, enrolling to a course and following the course itself on the LMS.

    It is possible to do the same with Moodle or any other LMS exposing an enrollment API, at the cost of writing a custom LMS handler backend.

    Prerequisites

    This connection requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that @@ -36,7 +36,7 @@ value other then manual.

    • Type: enum(string)
    • Required: No
    • Value: for example ["languages"]

    Technical support

    If you encounter an issue with this documentation or the backends included in Richie, please open an issue on our repository.

    If you need a custom backend, you can submit a PR or open an issue and we will consider adding it.

    - - + + \ No newline at end of file diff --git a/docs/2.21.0/lms-connection/index.html b/docs/2.21.0/lms-connection/index.html index 241efb47be..179820d5ca 100644 --- a/docs/2.21.0/lms-connection/index.html +++ b/docs/2.21.0/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with one or more LMS | Richie - - + +
    -
    Version: 2.21.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated +

    Version: 2.21.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated seamless experience.

    As of today, each approach has been implemented for OpenEdX but the same could be done for other LMSes like Moodle, at the cost of minor adaptations.

    1. Displaying connection status

    OpenEdX can be configured to allow CORS requests. Doing so allows Richie to retrieve a user's connection status from OpenEdx and display the user's profile information directly on the Richie @@ -31,7 +31,7 @@ docker-compose.

    If you want to activate seamless enrollment locally for development, you will need to set up TLS domains for both Richie and OpenEdX. To do this, head over to our guide on setting-up TLS connections for Richie and OpenEdX.

    - - + + \ No newline at end of file diff --git a/docs/2.21.0/native-installation/index.html b/docs/2.21.0/native-installation/index.html index 151442ee98..6883597303 100644 --- a/docs/2.21.0/native-installation/index.html +++ b/docs/2.21.0/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.21.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.21.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native installation instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.21.0/synchronizing-course-runs/index.html b/docs/2.21.0/synchronizing-course-runs/index.html index 1a6bde849b..08749bfb20 100644 --- a/docs/2.21.0/synchronizing-course-runs/index.html +++ b/docs/2.21.0/synchronizing-course-runs/index.html @@ -4,12 +4,12 @@ Synchronizing course runs between Richie and OpenEdX | Richie - - + +
    -
    Version: 2.21.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the +

    Version: 2.21.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the RICHIE_COURSE_RUN_SYNC_SECRETS setting with one or more secrets:

    RICHIE_COURSE_RUN_SYNC_SECRETS = ["SharedSecret", "OtherSharedSecret"]

    This setting collects several secrets in order to allow rotating them without any downtime. Any of the secrets listed in this setting can be used to sign your queries.

    Your secret should be shared with the LMS or distant system that needs to synchronize its course runs with the Richie instance. Richie will try the declared secrets one by one until it finds @@ -29,7 +29,7 @@ course run is modified and published.

    Or you can use the Richie Open edX Synchronization which is based on the following code sample and also includes the enrollment count.

    Given a COURSE_HOOK setting defined as follows in your OpenEdX instance:

    COURSE_HOOK = {
    "secret": "SharedSecret",
    "url": "https://richie.example.com/api/v1.0/course-runs-sync/",
    }

    The code for the synchronization function in OpenEdX could look like this:

    import hashlib
    import hmac
    import json

    from django.conf import settings

    from microsite_configuration import microsite
    import requests
    from xmodule.modulestore.django import modulestore


    def update_course(course_key, *args, **kwargs):
    """Synchronize an OpenEdX course, identified by its course key, with a Richie instance."""
    course = modulestore().get_course(course_key)
    edxapp_domain = microsite.get_value("site_domain", settings.LMS_BASE)

    data = {
    "resource_link": "https://{:s}/courses/{!s}/info".format(
    edxapp_domain, course_key
    ),
    "start": course.start and course.start.isoformat(),
    "end": course.end and course.end.isoformat(),
    "enrollment_start": course.enrollment_start and course.enrollment_start.isoformat(),
    "enrollment_end": course.enrollment_end and course.enrollment_end.isoformat(),
    "languages": [course.language or settings.LANGUAGE_CODE],
    }

    signature = hmac.new(
    setting.COURSE_HOOK["secret"].encode("utf-8"),
    msg=json.dumps(data).encode("utf-8"),
    digestmod=hashlib.sha256,
    ).hexdigest()

    response = requests.post(
    setting.COURSE_HOOK["url"],
    json=data,
    headers={"Authorization": "SIG-HMAC-SHA256 {:s}".format(signature)},
    )

    Thanks to the signal emitted in OpenEdX, this function can then be triggered each time a course is modified and published:

    from django.dispatch import receiver
    from xmodule.modulestore.django import SignalHandler


    @receiver(SignalHandler.course_published, dispatch_uid='update_course_on_publish')
    def update_course_on_publish(sender, course_key, **kwargs):
    update_course(course_key)
    - - + + \ No newline at end of file diff --git a/docs/2.21.0/tls-connection/index.html b/docs/2.21.0/tls-connection/index.html index a78c6ced5d..a44eee8734 100644 --- a/docs/2.21.0/tls-connection/index.html +++ b/docs/2.21.0/tls-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie and OpenEdX over TLS for development | Richie - - + +
    -
    Version: 2.21.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 +

    Version: 2.21.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 and uses a fake LMS backend (base.BaseLMSBackend) as you can see if you check the RICHIE_LMS_BACKENDS setting in env.d/development.

    This base backend uses session storage to fake enrollments to course runs.

    If you want to test real enrollments to an OpenEdX instance hosted on an external domain, OpenEdX will need to generate a CORS CSRF Cookie. This cookie is flagged as secure, which implies that @@ -32,7 +32,7 @@ to share cookies with the OpenEdx application to allow CORS CSRF Protected XHR requests.

    4. Start Richie and OpenEdx over SSL

    Now, the OpenEdx application should respond on https://edx.local.dev:8073, and Richie on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. The next time you want to use SSL, you can run the following command on both the Richie and OpenEdX projects:

    $ make run-ssl

    Of course, you can still run apps without ssl by using:

    $ make run
    - - + + \ No newline at end of file diff --git a/docs/2.21.0/web-analytics/index.html b/docs/2.21.0/web-analytics/index.html index eb95bc83dd..f86c1aaae7 100644 --- a/docs/2.21.0/web-analytics/index.html +++ b/docs/2.21.0/web-analytics/index.html @@ -4,19 +4,19 @@ Add web analytics to your site | Richie - - + +
    -
    Version: 2.21.0

    Add web analytics to your site

    Richie has native support to Google Universal Analytics and Google Tag Manager Web Analytics solutions. +

    Version: 2.21.0

    Add web analytics to your site

    Richie has native support to Google Universal Analytics and Google Tag Manager Web Analytics solutions. The purpose of this file is to explain how you can enable one of the supported Web Analytics providers and how you can extend Richie with an alternative solution.

    Google Universal Analytics

    Next, it is described how you can configure the Google Universal Analytics on your Richie site.

    Add the WEB_ANALYTICS setting, with the Google Universal Analytics configuration. From the next example replace TRACKING_ID with your tracking id code.

    {
    'google_universal_analytics': {
    'tracking_id': 'TRACKING_ID',
    }
    }

    The current Google Universal Analytics implementation also includes custom dimensions. Those dimensions permit you to create further analyses on Google Universal Analytics or even use them to create custom reports. Custom dimensions with a value as example:

    • Organizations codes - UNIV_LISBON | UNIV_PORTO
    • Course code - COURSE_XPTO
    • Course runs titles - Summer edition | Winter edition
    • Course runs resource links - http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/info
    • Page title - Introduction to Programming

    Google Tag

    It is possible to configure the Google Tag, gtag.js, on your Richie site.

    Add the WEB_ANALYTICS setting, with the Google Tag configuration like for example:

    {
    'google_tag': {
    'tracking_id': 'TRACKING_ID',
    }
    }

    And don't forget to replace the TRACKING_ID with your tracking id/code from Google Ads, Google Analytics, or other Google product compatible with the gtag.js.

    The Google Tag is initialized with custom dimensions like the Google Universal Analytics.

    Google Tag Manager

    Next, it is described how you can configure the Google Tag Manager, gtm.js, on your Richie site.

    Add the WEB_ANALYTICS setting, with the Google Tag Manager configuration, for example:

    {
    'google_tag_manager': {
    'tracking_id': 'TRACKING_ID',
    }
    }

    And don't forget to replace the TRACKING_ID with your GTM tracking id/code.

    The current Google Tag Manager implementation also defines a custom dimensions like the Google Universal Analytics.

    If you want to use the Environments feature of the Google Tag Manager, you need to include the environment key with its value on google_tag_manager dict inside the WEB_ANALYTICS setting.

    The environments feature in Google Tag Manager is ideal for organizations that want to preview their container changes in a test environment before those changes are published.

    {
    'google_tag_manager': {
    'tracking_id': 'TRACKING_ID',
    'environment': '&gtm_auth=aaaaaaaaaaaaaaaa&gtm_preview=env-99&gtm_cookies_win=x';
    }
    }

    Multiple Web Analytics at the same time

    It is possible to configure several web analytics solutions at the same time or the same solution with different tracking identifications.

    WEB_ANALYTICS setting example to have both Google Universal Analytics and Google Tag Manager:

    {
    'google_universal_analytics': {
    'tracking_id': 'UA-TRACKING_ID',
    },
    'google_tag_manager': {
    'tracking_id': 'GTM-TRACKING_ID',
    }
    }

    Location of the web analytics javascript

    Each web analytics js code can be put on the footer (default value), to put the Javascript on HTML body footer, or header, to put the Javascript code at the end of the HTML head.

    Update the WEB_ANALYTICS setting, like:

    {
    'google_universal_analytics': {
    'tracking_id': 'UA-TRACKING_ID',
    'location': 'footer,
    },
    }

    Add a new Web Analytics solution

    In this section it's described how you can add support to a different Web Analytics solution.

    • override the richie/web_analytics.html template
    • define the WEB_ANALYTICS setting with a value that represents your solution, eg. my-custom-web-analytics-software
    • define the WEB_ANALYTICS setting with your tracking identification
    • optionally change location with footer (default) or head value
    {
    'my-custom-web-analytics-software': {
    'tracking_id': 'MY_CUSTOM_TRACKING_ID',
    'location': 'footer,
    },
    }
    • Example of a richie/web_analytics.html file customization that prints to the browser console log the dimension keys and values:
    <script type="text/javascript">
    {% for dimension_key, dimension_value_list in WEB_ANALYTICS.DIMENSIONS.items %}
    console.log("dimension: index '{{forloop.counter}}' with key '{{ dimension_key }}' with value '{{ dimension_value_list|join:" | " }}'");
    {% endfor %}
    </script>

    Output:

    dimension: index '1' with key 'organizations_codes' with value 'COMPATIBLE-EVEN-KEELED-UTILIZATION-19 | FOCUSED-NEXT-GENERATION-FUNCTIONALITIES-22 | UNIVERSAL-MODULAR-LOCAL-AREA-NETWORK-23'
    dimension: index '2' with key 'course_code' with value '00017'
    dimension: index '3' with key 'course_runs_titles' with value 'Run 0'
    dimension: index '4' with key 'course_runs_resource_links' with value ''
    dimension: index '5' with key 'page_title' with value 'Business-focused zero-defect application'

    But you can also contribute to Richie by creating a pull request to add support for a different web analytics solution. In this last case, you have to edit directly the richie/web_analytics.html template.

    Example of an override of the richie/web_analytics.html file:

    {% extends "richie/web_analytics.html" %}
    {% block web_analytics_additional_providers %}
    {% if provider == "my_custom_web_analytics_software_provider" %}
    <script type="text/javascript" src="{% static 'myapp/js/custom_web_analytics_software.js' %}">
    <script type="text/javascript">
    // javascript code that startups the custom web analytics software
    </script>
    {% endif %}
    {% endblock web_analytics_additional_providers %}

    The web analytics dimensions are being added to the django context using the WEB_ANALYTICS.DIMENSIONS dictionary. Because each dimension value could have multiple values, then each dictionary value is a list. Web analytics dimensions dictionary keys:

    • organizations_codes
    • course_code
    • course_runs_titles
    • course_runs_resource_links
    • page_title

    Example, if you only need the organization codes on your custom richie/web_analytics.html file:

    <script type="text/javascript">
    console.log("organization codes: '{{ WEB_ANALYTICS.DIMENSIONS.organizations_codes |join:" | " }}");
    </script>

    The frontend code also sends events to the web analytics provider. Richie sends events when the user is enrolled on a course run. To support different providers, you need to create a similar file of src/frontend/js/utils/api/web-analytics/google_universal_analytics.ts and change the src/frontend/js/utils/api/web-analytics/index.ts file to include that newer provider.

    - - + + \ No newline at end of file diff --git a/docs/2.21.1/accessibility-testing/index.html b/docs/2.21.1/accessibility-testing/index.html new file mode 100644 index 0000000000..fd57d7da0b --- /dev/null +++ b/docs/2.21.1/accessibility-testing/index.html @@ -0,0 +1,16 @@ + + + + + +Automated accessibility checks | Richie + + + + +
    +
    Version: 2.21.1

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems on any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two ways to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + + + \ No newline at end of file diff --git a/docs/2.21.1/api/course-run-synchronization-api/index.html b/docs/2.21.1/api/course-run-synchronization-api/index.html new file mode 100644 index 0000000000..091e12f554 --- /dev/null +++ b/docs/2.21.1/api/course-run-synchronization-api/index.html @@ -0,0 +1,25 @@ + + + + + +Course run synchronization API | Richie + + + + +
    +
    Version: 2.21.1

    Course run synchronization API

    API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

    Synchronization endpoint [/api/1.0/course-runs-sync]

    This documentation describes version "1.0" of this API endpoint.

    Synchronize a course run [POST]

    It takes a JSON object containing the course run details:

    • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - +url of the course syllabus on the LMS from which a unique course identifier can be extracted
    • start: 2018-02-01T06:00:00Z (string, optional) - ISO 8601 date, when this session of the +course starts
    • end: 2018-02-28T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course +ends
    • enrollment_start: 2018-01-01T06:00:00Z (string, optional) - ISO 8601 date, when enrollment +for this session of the course starts
    • enrollment_end: 2018-01-31T06:00:00Z (string, optional) - ISO 8601 date, when enrollment for +this session of the course ends
    • languages: ['fr', 'en'] (array[string], required) - ISO 639-1 code (2 letters) for the course's +languages
    • Request (application/json)

      • Headers

        • Authorization: SIG-HMAC-SHA256 xxxxxxx (string, required) - Authorization header +containing the digest of the utf-8 encoded json representation of the submitted data +for the given secret key and SHA256 digest algorithm (see [synchronizing-course-runs] +for an example).
      • Body

          {
        "resource_link": "https://lms.example.com/courses/course-v1:001+001+001/info",
        "start": "2021-02-01T00:00:00Z",
        "end": "2021-02-31T23:59:59Z",
        "enrollment_start": "2021-01-01T00:00:00Z",
        "enrollment_end": "2021-01-31T23:59:59Z",
        "languages": ["en", "fr"]
        }
    • Response 200 (application/json)

      • Body

          {
        "success": True
        }
    + + + + \ No newline at end of file diff --git a/docs/2.21.1/building-the-frontend/index.html b/docs/2.21.1/building-the-frontend/index.html new file mode 100644 index 0000000000..3467a09281 --- /dev/null +++ b/docs/2.21.1/building-the-frontend/index.html @@ -0,0 +1,16 @@ + + + + + +Building Richie's frontend in your own project | Richie + + + + +
    +
    Version: 2.21.1

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + + + \ No newline at end of file diff --git a/docs/2.21.1/contributing-guide/index.html b/docs/2.21.1/contributing-guide/index.html new file mode 100644 index 0000000000..89f683d218 --- /dev/null +++ b/docs/2.21.1/contributing-guide/index.html @@ -0,0 +1,22 @@ + + + + + +Contributing guide | Richie + + + + +
    +
    Version: 2.21.1

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically +creates a fresh database named richie and performs database migrations. Each time a new +database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the +application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the +bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker +documentation

    + + + + \ No newline at end of file diff --git a/docs/2.21.1/cookiecutter/index.html b/docs/2.21.1/cookiecutter/index.html new file mode 100644 index 0000000000..9a59db405d --- /dev/null +++ b/docs/2.21.1/cookiecutter/index.html @@ -0,0 +1,36 @@ + + + + + +Start your own site | Richie + + + + +
    +
    Version: 2.21.1

    Start your own site

    We use Cookiecutter to help you +set up a production-ready learning portal website based on +Richie in seconds.

    Run Cookiecutter

    There are 2 options to run Cookiecutter:

    While you think of it, navigate to the directory in which you want to create +your site factory:

    cd /path/to/your/code/directory

    If you chose to install Cookiecutter, you can now run it against our +template as follows:

    cookiecutter gh:openfun/richie --directory cookiecutter  --checkout v2.21.1

    If you didn't want to install it on your machine, we provide a Docker image +built with our own repository that you can use as follows:

    docker run --rm -it -u $(id -u):$(id -g) -v $PWD:/app \
    fundocker/cookiecutter gh:openfun/richie --directory cookiecutter --checkout v2.21.1

    The --directory option is to indicate that our Cookiecutter template is in +a cookiecutter directory inside our git repository and not at the root.

    You will be prompted to enter an organization name, which will determine the +name of your repository. For example, if you choose "foo" as organization +name, your repository will be named foo-richie-site-factory. It's +nice if you keep it that way so all richie site factories follow this +convention.

    When you confirm the organization name, Cookiecutter will generate your +project from the Cookiecutter template and place it at the level where you +currently are.

    Bootstrap your project

    Enter the newly created project and add a new site to your site factory:

    cd foo-richie-site-factory
    make add-site

    This script also uses Cookiecutter against our site template.

    Once your new site is created, activate it:

    bin/activate

    Now bootstrap the site to build its docker image, create its media folder, +database, etc.:

    make bootstrap

    Once the bootstrap phase is finished, you should be able to view the site at +localhost:8070.

    You can create a full fledge demo to test your site by running:

    make demo-site

    Note that the README of your newly created site factory contains detailed +information about how to configure and run a site.

    Once you're happy with your site, don't forget to backup your work e.g. by +committing it and pushing it to a new git repository.

    Theming

    You probably want to change the default theme. The cookiecutter adds an extra scss frontend folder with a couple of templates that you can use to change the default styling of the site.

    • sites/<site>/src/frontend/scss/extras/colors/_palette.scss
    • sites/<site>/src/frontend/scss/extras/colors/_theme.scss

    To change the default logo of the site, you need to create the folder sites/<site>/src/backend/base/static/richie/images and then override the new logo.png picture.

    For more advanced customization, refer to our recipes:

    Update your Richie site factory

    If we later improve our scripts, you will be able to update your own site +factory by replaying Cookiecutter. This will override your files in the +project's scaffolding but, don't worry, it will respect all the sites you +will have created in the sites directory:

    cookiecutter --overwrite-if-exists gh:openfun/richie --directory=cookiecutter

    Help us improve this project

    After starting your project, please submit an issue let us know how it went and +what other features we should add to make it better.

    + + + + \ No newline at end of file diff --git a/docs/2.21.1/css-guidelines/index.html b/docs/2.21.1/css-guidelines/index.html new file mode 100644 index 0000000000..6c908d1ee2 --- /dev/null +++ b/docs/2.21.1/css-guidelines/index.html @@ -0,0 +1,16 @@ + + + + + +CSS Guidelines | Richie + + + + +
    +
    Version: 2.21.1

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + + + \ No newline at end of file diff --git a/docs/2.21.1/discover/index.html b/docs/2.21.1/discover/index.html new file mode 100644 index 0000000000..1f8f99eb7f --- /dev/null +++ b/docs/2.21.1/discover/index.html @@ -0,0 +1,27 @@ + + + + + +Discover Richie | Richie + + + + +
    +
    Version: 2.21.1

    Discover Richie

    Learning Management Systems (LMS) are great tools for hosting and playing interactive online +courses and MOOCs.

    However, if you need to build a complete website with flexible content to aggregate your courses, +in several languages and from different sources, you will soon need a CMS.

    At "France Université Numérique", we wanted to build an OpenSource portal with Python and +Django. That's why we built Richie on top of DjangoCMS, one of +the best CMS on the market, as a toolbox to easily create full fledged websites with a catalog of +online courses.

    screenshot of richie demo site

    Among the features that Richie offers out of the box:

    • multi-lingual by default,
    • advanced access rights and moderation,
    • catalog of courses synchronized with one or more LMS instances,
    • search engine based on Elasticsearch and pre-configured to offer full-text queries, +multi-facetting, auto-complete,...
    • flexible custom pages for courses, organizations, categories, teachers, blog posts, +programs (and their inter-relations),
    • Extensible with any third-party DjangoCMS plugin or any third-party Django application.

    Quick preview

    If you're looking for a quick preview of Richie, you can take a look and have a tour of +Richie on our dedicated demo site.

    It is connected back-to-back with a demo of OpenEdX running on +OpenEdX Docker.

    Two users are available for testing:

    • admin: admin@example.com/admin
    • student: edx@example.com/edx

    The database is regularly flushed.

    Start your own site

    The next step after discovering Richie on the demo is to start your own project. We provide a +production-ready template based on Cookiecutter that +allows you to generate your project in seconds.

    Follow the guide on starting your own Richie site with Cookiecutter.

    Once you created a new site, you will be able to fully customize it:

    + + + + \ No newline at end of file diff --git a/docs/2.21.1/displaying-connection-status/index.html b/docs/2.21.1/displaying-connection-status/index.html new file mode 100644 index 0000000000..4506041b08 --- /dev/null +++ b/docs/2.21.1/displaying-connection-status/index.html @@ -0,0 +1,32 @@ + + + + + +Displaying OpenEdX connection status in Richie | Richie + + + + +
    +
    Version: 2.21.1

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status +and display profile information for the logged-in user in Richie.

    In Richie, the only users that are actually authenticated on the DjangoCMS instance producing the +site, are editors and staff users. Your instructors and learners will not have user accounts on +Richie, but authentication is delegated to a remote service that can be OpenEdX, KeyCloack, or +your own centralized identity management service.

    In the following, we will explain how to use OpenEdX as your authentication delegation service.

    Prerequisites

    Richie will need to make CORS requests to the OpenEdX instance. As a consequence, you need to +activate CORS requests on your OpenEdX instance:

    FEATURES = {
    ...
    "ENABLE_CORS_HEADERS": True,
    }

    Then, make sure the following settings are set as follows on your OpenEdX instance:

    CORS_ALLOW_CREDENTIALS = True
    CORS_ALLOW_INSECURE = False
    CORS_ORIGIN_ALLOW_ALL = False
    CORS_ORIGIN_WHITELIST: ["richie.example.com"] # The domain on which Richie is hosted

    Allow redirects

    When Richie sends the user to the OpenEdX instance for authentication, and wants OpenEdX to +redirect the user back to Richie after a successful login or signup, it prefixes the path with +/richie. Adding the following rule to your Nginx server (or equivalent) and replacing the +richie host by yours will allow this redirect to follow through to your Richie instance:

    rewrite ^/richie/(.*)$ https://richie.example.com/$1 permanent;

    Configure authentication delegation

    Now, on your Richie instance, you need to configure the service to which Richie will delegate +authentication using the RICHIE_AUTHENTICATION_DELEGATION setting:

    RICHIE_AUTHENTICATION_DELEGATION = {
    "BASE_URL": "https://lms.example.com",
    "BACKEND": "openedx-hawthorn",
    "PROFILE_URLS": {
    "dashboard": {
    "label": _("Dashboard"),
    "href": "{base_url:s}/dashboard",
    },
    },
    }

    The following should help you understand how to configure this setting:

    BASE_URL

    The base url on which the OpenEdX instance is hosted. This is used to construct the complete url +of the login/signup pages to which the frontend application will send the user for authentication.

    BACKEND

    The name of the ReactJS backend to use for the targeted LMS.

    • Type: string
    • Required: Yes
    • Value: Richie ships with the following Javascript backends:
      • openedx-dogwood: backend for OpenEdX versions equal to dogwood or eucalyptus
      • openedx-hawthorn: backend for OpenEdX versions equal to hawthorn or higher
      • openedx-fonzie: backend for OpenEdX via Fonzie +(extra user info and JWT tokens)
      • base: fake backend for development purposes

    PROFILE_URLS

    Mapping definition of custom links presented to the logged-in user as a dropdown menu when he/she +clicks on his/her username in Richie's page header.

    Links order will be respected to build the dropdown menu.

    • Type: dictionary

    • Required: No

    • Value: For example, to emulate the links proposed in OpenEdX, you can configure this setting +as follows:

          {
      "dashboard": {
      "label": _("Dashboard"),
      "href": "{base_url:s}/dashboard",
      },
      "profile": {
      "label": _("Profile"),
      "href": "{base_url:s}/u/(username)",
      },
      "account": {
      "label": _("Account"),
      "href": "{base_url:s}/account/settings",
      }
      }

      The base_url variable is used as a Python format parameter and will be replaced by the value +set for the above authentication delegation BASE_URL setting.

      If you need to bind user data into a url, wrap the property between brackets. For example, the +link configured above for the profile page {base_url:s}/u/(username) would point to +https://lms.example.com/u/johndoe for a user carrying the username johndoe.

    + + + + \ No newline at end of file diff --git a/docs/2.21.1/django-react-interop/index.html b/docs/2.21.1/django-react-interop/index.html new file mode 100644 index 0000000000..bda0e2d10f --- /dev/null +++ b/docs/2.21.1/django-react-interop/index.html @@ -0,0 +1,16 @@ + + + + + +Connecting React components with Django | Richie + + + + +
    +
    Version: 2.21.1

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + + + \ No newline at end of file diff --git a/docs/2.21.1/docker-development/index.html b/docs/2.21.1/docker-development/index.html new file mode 100644 index 0000000000..e46f99c4ef --- /dev/null +++ b/docs/2.21.1/docker-development/index.html @@ -0,0 +1,35 @@ + + + + + +Developing Richie with Docker | Richie + + + + +
    +
    Version: 2.21.1

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +Configurations for +different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have +sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the +docker-compose exec command (we use a sugar script here, see next section):

    $ bin/exec [app|postgresql|mysql|elasticsearch] bash

    While developing on Richie, you will also need to run a Django shell and it +has to be done in the app container (we use a sugar script here, see next +section):

    $ bin/run app python sandbox/manage.py shell

    Using sugar scripts

    While developing using Docker, you will fall into permission issues if you mount +the code directory as a volume in the container. Indeed, the Docker engine will, +by default, run the containers using the root user. Any file created or +updated by the app container on your host, as a result of the volume mounts, +will be owned by the local root user. One way to solve this is to use the +--user="$(id -u)" flag when calling the docker-compose run or +docker-compose exec commands. By using the user flag trick, the running +container user ID will match your local user ID. But, as it's repetitive and +error-prone, we provide shortcuts that we call our "sugar scripts":

    • bin/run: is a shortcut for docker-compose run --rm --user="$(id -u)"
    • bin/exec: is a shortcut for docker-compose exec --user="$(id -u)"
    • bin/pylint: runs pylint in the app service using the test docker-compose +file
    • bin/pytest: runs pytest in the app service using the test docker-compose +file

    Cleanup

    If you work on the Docker configuration and make repeated modifications, +remember to periodically clean the unused docker images and containers by +running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the +/etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    + + + + \ No newline at end of file diff --git a/docs/2.21.1/filters-customization/index.html b/docs/2.21.1/filters-customization/index.html new file mode 100644 index 0000000000..c6851dbf50 --- /dev/null +++ b/docs/2.21.1/filters-customization/index.html @@ -0,0 +1,54 @@ + + + + + +Customizing search filters | Richie + + + + +
    +
    Version: 2.21.1

    Customizing search filters

    You may want to customize the filters on the left side bar of the search page.

    Richie makes it easy to choose which filters you want to display among the existing filters +and in which order. You can also configure the existing filters to change their title or the +way they behave. Lastly, you can completely override a filter or create your own custom filter +from scratch.

    Filters configuration

    Filters must first be defined in the FILTERS_CONFIGURATION setting. It is a dictionary defining +for each filter, a predefined class in the code where the filter is implemented and the +parameters to apply to this class when instantiating it.

    Let's study a few examples of filters in the default configuration:

    FILTERS_CONFIGURATION = {
    ...
    "pace": {
    "class": "richie.apps.search.filter_definitions.StaticChoicesFilterDefinition",
    "params": {
    "fragment_map": {
    "self-paced": [{"bool": {"must_not": {"exists": {"field": "pace"}}}}],
    "lt-1h": [{"range": {"pace": {"lt": 60}}}],
    "1h-2h": [{"range": {"pace": {"gte": 60, "lte": 120}}}],
    "gt-2h": [{"range": {"pace": {"gt": 120}}}],
    },
    "human_name": _("Weekly pace"),
    "min_doc_count": 0,
    "sorting": "conf",
    "values": {
    "self-paced": _("Self-paced"),
    "lt-1h": _("Less than one hour"),
    "1h-2h": _("One to two hours"),
    "gt-2h": _("More than two hours"),
    },
    },
    },
    ...
    }

    This filter uses the StaticChoicesFilterDefinition filter definition class and allows filtering +on the pace field present in the Elasticsearch index. The values parameter defines 4 ranges +and their human readable format that will appear as 4 filtering options to the user.

    The fragment_map parameter defines a fragment of the Elasticsearch query to apply on the pace +field when one of these options is selected.

    The human_nameparameter defines how the filter is entitled. It is defined as a lazy i18n string +so that it can be translated.

    The sorting parameter determines how the facets are sorted in the left side panel of the filter:

    • conf: facets are sorted as defined in the values configuration parameter
    • count: facets are sorted according to the number of course results associated with each facet
    • name: facets are sorted by their name in alphabetical order

    The min_doc_count parameter defines how many associated results a facet must have at the minimum +before it is displayed as an option for the filter.

    Let's study another interesting example:

    FILTERS_CONFIGURATION = {
    ...
    "organizations": {
    "class": "richie.apps.search.filter_definitions.IndexableHierarchicalFilterDefinition",
    "params": {
    "human_name": _("Organizations"),
    "is_autocompletable": True,
    "is_drilldown": False,
    "is_searchable": True,
    "min_doc_count": 0,
    "reverse_id": "organizations",
    },
    },
    ...
    }

    This filter uses the IndexableHierarchicalFilterDefinition filter definition class and allows +filtering on the link between course pages and other pages identified by their IDs like for +example here Organization pages.

    In the example above, when an option is selected, results will only include the courses for which +the organizations field in the index is including the ID of the selected organization page.

    The reverse_id parameter should point to a page's reverse ID (see DjangoCMS documentation) in +the CMS. The filter will propose a filtering option for each children organization under this +page.

    The is_autocompletable field determines whether organizations should be searched and suggested +by the autocomplete feature (organizations must have an associated index and API endpoint for +autocompletion carrying the same name).

    The is_drilldown parameter determines whether the filter is limited to one active value at a +time or allows multi-facetting.

    The is_searchable field determines whether organizations filters should present a "more options" +button in case there are more facet options in results than can be displayed (organizations must +have an associated API endpoint for full-text search, carrying the same name).

    Lastly, let's look at nested filters which, as their name indicates, allow filtering on nested +fields.

    For example, in the course index, one of the fields is named course_runs and contains a list of +objects in the following format:

    "course_runs": [
    {
    "start": "2022-09-09T09:00:00.000000",
    "end": "2021-10-30T00:00:00.000000Z",
    "enrollment_start": "2022-08-01T09:00:00.000000Z",
    "enrollment_end": "2022-09-08T00:00:00.000000Z",
    "languages": ["en", "fr"],
    },
    {
    "start": "2023-03-01T09:00:00.000000",
    "end": "2023-06-03T00:00:00.000000Z",
    "enrollment_start": "2023-01-01T09:00:00.000000Z",
    "enrollment_end": "2023-03-01T00:00:00.000000Z",
    "languages": ["fr"],
    },
    ]

    If we want to filter courses that are available in the english language, we can thus configure the +following filter:

    FILTERS_CONFIGURATION = {
    ...
    "course_runs": {
    "class": "richie.apps.search.filter_definitions.NestingWrapper",
    "params": {
    "filters": {
    "languages": {
    "class": "richie.apps.search.filter_definitions.LanguagesFilterDefinition",
    "params": {
    "human_name": _("Languages"),
    # There are too many available languages to show them all, all the time.
    # Eg. 200 languages, 190+ of which will have 0 matching courses.
    "min_doc_count": 1,
    },
    },
    }
    },
    },
    ...
    }

    Filters presentation

    Which filters are displayed in the left side bar of the search page and in which order is defined +by the RICHIE_FILTERS_PRESENTATION setting.

    This setting is expecting a list of strings, which are the names of the filters as defined +in the FILTERS_CONFIGURATION setting decribed in the previous section. If it, for example, +contains the 3 filters presented in the previous section, we could define the following +presentation:

    RICHIE_FILTERS_PRESENTATION = ["organizations", "languages", "pace"]

    Writing your own custom filters

    You can write your own filters from scratch although we must warn you that it is not trivial +because it requires a good knowledge of Elasticsearch and studying the mapping defined in the +courses indexer.

    A filter is a class deriving from BaseFilterDefinition and defining methods to return the +information to display the filter and query fragments for elasticsearch:

    • get_form_fields: returns the form field instance that will be used to parse and validate this +filter's values from the querystring
    • get_query_fragment: returns the query fragment to use as filter in ElasticSearch
    • get_aggs_fragment: returns the query fragment to use to extract facets from +ElasticSearch aggregations
    • get_facet_info: returns the dynamic facet information from a filter's Elasticsearch facet +results. Together with the facet's static information, it will be used to display the filter +in its current status in the left side panel of the search page.

    We will not go into more details here about how filter definition classes work, but you can refer +to the code of the existing filters as good examples of what is possible. The code, although not +trivial, was given much care and includes many comments in an attempt to help writing new custom +filters. Of course, don't hesitate to ask for help by +opening an issue!

    + + + + \ No newline at end of file diff --git a/docs/2.21.1/frontend-overrides/index.html b/docs/2.21.1/frontend-overrides/index.html new file mode 100644 index 0000000000..47f593e611 --- /dev/null +++ b/docs/2.21.1/frontend-overrides/index.html @@ -0,0 +1,16 @@ + + + + + +Overriding frontend components | Richie + + + + +
    +
    Version: 2.21.1

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + + + \ No newline at end of file diff --git a/docs/2.21.1/installation/index.html b/docs/2.21.1/installation/index.html new file mode 100644 index 0000000000..9ac1a80027 --- /dev/null +++ b/docs/2.21.1/installation/index.html @@ -0,0 +1,41 @@ + + + + + +Installing Richie for development | Richie + + + + +
    +
    Version: 2.21.1

    Installing Richie for development

    Richie is a container-native application but can also be installed +on your machine.

    For development, the project is defined using a +docker-compose file and consists of:

    • 3 running services:

      • database: postgresql or mysql at your preference,
      • elasticsearch: the search engine,
      • app: the actual DjangoCMS project with all our application code.
    • 2 containers for building purposes:

      • node: used for front-end related tasks, i.e. transpiling +TypeScript sources, bundling them into a JS package, and building the +CSS files from Sass sources,
      • crowdin: used to upload and retrieve i18n files to and from the +Crowding service that we use to crowd source +translations,

    At "France Université Numérique", we deploy our applications on Kubernetes +using Arnold.

    Docker

    First, make sure you have a recent version of Docker and +Docker Compose installed on your +laptop:

    $ docker -v
    Docker version 20.10.12, build e91ed57

    $ docker-compose --version
    docker-compose version 1.29.2, build 5becea4c

    ⚠️ You may need to run the following commands with sudo but this can be +avoided by assigning your user to the docker group.

    Project bootstrap

    The easiest way to start working on the project is to use our Makefile:

    $ make bootstrap

    This command builds the app container, installs front-end and back-end +dependencies, builds the front-end application and styles, and performs +database migrations. It's a good idea to use this command each time you are +pulling code from the project repository to avoid dependency-related or +migration-related issues.

    Now that your Docker services are ready to be used, start the full CMS by +running:

    $ make run

    Adding content

    Once the CMS is up and running, you can create a superuser account:

    $ make superuser

    You can create a basic demo site by running:

    $ make demo-site

    Note that if you don't create the demo site and start from a blank CMS, you +will get some errors requesting you to create some required root pages. So it +is easier as a first approach to test the CMS with the demo site.

    You should be able to view the site at localhost:8070

    Connecting Richie to an LMS

    It is possible to use Richie as a catalogue aggregating courses from one or +more LMS without any specific connection. In this case, each course run in +the catalogue points to a course on the LMS, and the LMS points back to the +catalogue to browse courses.

    This approach is used for example on https://www.fun-campus.fr or +https://catalogue.edulib.org.

    For a seamless user experience, it is possible to connect a Richie instance +to an OpenEdX instance (or some other LMS like Moodle at the cost of minor +adaptations), in several ways that we explain in the +LMS connection guide.

    This approach is used for example on https://www.fun-mooc.fr or +https://www.nau.edu.pt.

    + + + + \ No newline at end of file diff --git a/docs/2.21.1/internationalization/index.html b/docs/2.21.1/internationalization/index.html new file mode 100644 index 0000000000..a587b7cb87 --- /dev/null +++ b/docs/2.21.1/internationalization/index.html @@ -0,0 +1,28 @@ + + + + + +Internationalization | Richie + + + + +
    +
    Version: 2.21.1

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +All translations are hosted at https://i18n.richie.education, which allows translators and +proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and +fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, +look for the project called "Richie", select the language +on which you wish to contribute and click the "Join" button as demonstrated below:

    How to join Richie on Crowdin

    We will then review you application and you should soon start translating strings!

    For more information on how Crowdin works, you can refer to +their documentation.

    Add a new language

    If Richie is not yet translated in the language you want, let us know by clicking the "contact" +link on Richie's Crowdin profile page and we will consider adding +it.

    If you request a new language, the Richie community will expect you to keep this language +up-to-date each time strings are modified or new strings are added, and this before each +release.

    Before asking for a new language, make sure it does not already exist. If your language already +exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider +contributing on the existing language if your resources to contribute are limited.

    + + + + \ No newline at end of file diff --git a/docs/2.21.1/joanie-connection/index.html b/docs/2.21.1/joanie-connection/index.html new file mode 100644 index 0000000000..11646d694f --- /dev/null +++ b/docs/2.21.1/joanie-connection/index.html @@ -0,0 +1,46 @@ + + + + + +Joanie Connection | Richie + + + + +
    +
    Version: 2.21.1

    Joanie Connection

    Joanie delivers an API able to manage course +enrollment/subscription, payment and certificates delivery. Richie can be configured to display +course runs and micro-credentials managed through Joanie.

    In fact, Richie treats Joanie almost like a LMS backend that's why settings +are similars.

    Configuring Joanie

    All settings related to Joanie have to be declared in the JOANIE_BACKEND dictionnary +within settings.py.

    JOANIE_BACKEND = {
    "BASE_URL": values.Value(environ_name="JOANIE_BASE_URL", environ_prefix=None),
    "BACKEND": values.Value("richie.apps.courses.lms.joanie.JoanieBackend", environ_name="JOANIE_BACKEND", environ_prefix=None),
    "JS_BACKEND": values.Value("joanie", environ_name="JOANIE_JS_BACKEND", environ_prefix=None),
    "COURSE_REGEX": values.Value(
    r"^.*/api/v1.0/(?P<resource_type>(course-runs|products))/(?P<resource_id>[^/]*)/?$",
    environ_name="JOANIE_COURSE_REGEX",
    environ_prefix=None,
    ),
    "JS_COURSE_REGEX": values.Value(
    r"^.*/api/v1.0/(course-runs|products)/([^/]*)/?$",
    environ_name="JOANIE_JS_COURSE_REGEX",
    environ_prefix=None,
    ),
    # Course runs synchronization
    "COURSE_RUN_SYNC_NO_UPDATE_FIELDS": [],
    "DEFAULT_COURSE_RUN_SYNC_MODE": "sync_to_public",
    }
    ...

    As mentioned earlier, Joanie is treated as a LMS by Richie, so we have to bind Joanie settings with +LMS backends settings. We achieve this by dynamically appending the JOANIE_BACKEND setting value +into the RICHIE_LMS_BACKENDS list, if Joanie is enabled. To understand this point, you can take a +look at the post_setup method of the Base class configuration into settings.py.

    Here they are all settings available into JOANIE_BACKEND:

    BASE_URL

    The base url on which the Joanie instance is hosted. This is used to construct the complete url of +the API endpoint on which requests are made by Richie's frontend application. Richie checks if this +settings is set to know if Joanie should be enabled or not.

    BACKEND

    The path to a Python class serving as Joanie backend. You should not need to change this setting +until you want to customize the behavior of the python Joanie backend.

    • Type: string
    • Required: No
    • Value: By default it is richie.apps.courses.lms.joanie.JoanieBackend

    JS_BACKEND

    The name of the ReactJS backend to use Joanie. You should not need to change this setting +until you want to customize the behavior of the js Joanie backend.

    • Type: string
    • Required: No
    • Value: By default it is joanie.

    COURSE_REGEX

    A python regex that should match the ressource api urls of Joanie and return a +resource_type named group ("course_runs" or "products") and also a resource_id +named group corresponding to the resource uuid.

    • Type: string
    • Required: No
    • Value: for example r"^.*/api/v1.0/(?P<resource_type>(course-runs|products))/(?P<resource_id>[^/]*)/?$"

    JS_COURSE_REGEX

    A Javascript regex that should match the ressource api urls of Joanie and return two +unnamed groups. The first one corresponds to the resource type ("course_runs" or "products") and +the second one corresponds to the resource uuid.

    • Type: string
    • Required: No
    • Value: for example r"^.*/api/v1.0/(course-runs|products)/([^/]*)/?$"

    COURSE_RUN_SYNC_NO_UPDATE_FIELDS

    A list of fields that must only be set the first time a course run is synchronized. During this +first synchronization, a new course run is created in Richie and all fields sent to the API +endpoint via the payload are set on the object. For subsequent synchronization calls, the fields +listed in this setting are ignored and not synchronized. This can be used to allow modifying some +fields manually in Richie regardless of what OpenEdX sends after an initial value is set.

    Read documentation of the eponym LMS_BACKENDS settings, +to discover how it can be configured.

    DEFAULT_COURSE_RUN_SYNC_MODE

    Joanie resources (course runs and products) are all synchronized with Richie as a CourseRun. This +setting is used to set the value of the sync_mode field when a course run is created on Richie. +Read documentation of the eponym LMS_BACKENDS settings, +to discover how it can be configured.

    Access Token

    Lifetime configuration

    Access Token is stored within the SessionStorage through react-query client persister. +By default, richie frontend considered access token as stale after 5 minutes. You can change this +value into settings.ts +by editing REACT_QUERY_SETTINGS.staleTimes.session.

    To always have a valid access token, you have to configure properly its stale time according to the +lifetime of the access token defined by your authentication server. For example, if your +authentication server is using djangorestframework-simplejwt to generate the access token, +its lifetime is 5 minutes by default.

    Technical support

    If you encounter an issue with this documentation, please +open an issue on our repository.

    + + + + \ No newline at end of file diff --git a/docs/2.21.1/lms-backends/index.html b/docs/2.21.1/lms-backends/index.html new file mode 100644 index 0000000000..4552060735 --- /dev/null +++ b/docs/2.21.1/lms-backends/index.html @@ -0,0 +1,42 @@ + + + + + +Configuring LMS Backends | Richie + + + + +
    +
    Version: 2.21.1

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless +experience between browsing the course catalog on Richie, enrolling to a course and following the +course itself on the LMS.

    It is possible to do the same with Moodle or any other LMS exposing an enrollment API, at the +cost of writing a custom LMS handler backend.

    Prerequisites

    This connection requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that +are both subdomains of the same root domain, e.g. richie.example.com and lms.example.com.

    OpenEdX will need to generate a CORS CSRF Cookie and this cookie is flagged as secure, which +implies that we are not able to use it without SSL connections.

    As a consequence, you need to configure your OpenEdX instance as follows:

    FEATURES = {
    ...
    "ENABLE_CORS_HEADERS": True,
    "ENABLE_CROSS_DOMAIN_CSRF_COOKIE": True,
    }

    CORS_ALLOW_CREDENTIALS = True
    CORS_ALLOW_INSECURE = False
    CORS_ORIGIN_WHITELIST: ["richie.example.com"] # The domain on which Richie is hosted

    CROSS_DOMAIN_CSRF_COOKIE_DOMAIN = ".example.com" # The parent domain shared by Richie and OpenEdX
    CROSS_DOMAIN_CSRF_COOKIE_NAME: "edx_csrf_token"
    SESSION_COOKIE_NAME: "edx_session"

    Configuring the LMS handler

    The LMSHandler utility acts as a proxy that routes queries to the correct LMS backend API, +based on a regex match on the URL of the course. It is configured via the RICHIE_LMS_BACKENDS +setting. As an example, here is how it would be configured to connect to an Ironwood OpenEdX +instance hosted on https://lms.example.com:

    RICHIE_LMS_BACKENDS=[
    {
    "BASE_URL": "https://lms.example.com",
    # Django
    "BACKEND": "richie.apps.courses.lms.edx.EdXLMSBackend",
    "COURSE_REGEX": r"^https://lms\.example\.com/courses/(?P<course_id>.*)/course/?$",
    # ReactJS
    "JS_BACKEND": "openedx-hawthorn",
    "JS_COURSE_REGEX": r"^https://lms\.example\.com/courses/(.*)/course/?$",
    # Course runs synchronization
    "COURSE_RUN_SYNC_NO_UPDATE_FIELDS": [],
    "DEFAULT_COURSE_RUN_SYNC_MODE": "sync_to_public",
    },
    ]

    The following should help you understand how to configure this setting:

    BASE_URL

    The base url on which the OpenEdX instance is hosted. This is used to construct the complete url +of the API endpoint on which the enrollment request is made by Richie's frontend application.

    BACKEND

    The path to a Python class serving as LMS backend for the targeted LMS.

    • Type: string
    • Required: Yes
    • Value: Richie ships with the following Python backends (custom backends can be written to fit +another specific LMS):
      • richie.apps.courses.lms.edx.EdXLMSBackend: backend for OpenEdX
      • richie.apps.courses.lms.base.BaseLMSBackend: fake backend for development purposes

    COURSE_REGEX

    A Python regex that should match the course syllabus urls of the targeted LMS and return a +course_id named group on the id of the course extracted from these urls.

    • Type: string
    • Required: Yes
    • Value: for example ^.*/courses/(?P<course_id>.*)/course/?$

    JS_BACKEND

    The name of the ReactJS backend to use for the targeted LMS.

    • Type: string
    • Required: Yes
    • Value: Richie ships with the following Javascript backends (custom backends can be written to +fit another specific LMS):
      • openedx-dogwood: backend for OpenEdX versions equal to dogwood or eucalyptus
      • openedx-hawthorn: backend for OpenEdX versions equal to hawthorn or higher
      • openedx-fonzie: backend for OpenEdX via Fonzie +(extra user info and JWT tokens)
      • dummy: fake backend for development purposes

    JS_COURSE_REGEX

    A Javascript regex that should match the course syllabus urls of the targeted LMS and return an +unnamed group on the id of the course extracted from these urls.

    • Type: string
    • Required: Yes
    • Value: for example ^.*/courses/(.*)/course/?$

    DEFAULT_COURSE_RUN_SYNC_MODE

    When a course run is created, this setting is used to set the value of the sync_mode field. +This value defines how the course runs synchronization script will impact this course run after +creation.

    • Type: enum(string)
    • Required: No
    • Value: possible values are manual, sync_to_draft and sync_to_public
      • manual: this course run is ignored by the course runs synchronization script
      • sync_to_draft: only the draft version of this course run is synchronized. A manual +publication is necessary for the update to be visible on the public site.
      • sync_to_public: the public version of this course run is updated by the synchronization +script. As a results, updates are directly visible on the public site without further +publication by a staff user in Richie.

    COURSE_RUN_SYNC_NO_UPDATE_FIELDS

    A list of fields that must only be set the first time a course run is synchronized. During this +first synchronization, a new course run is created in Richie and all fields sent to the API +endpoint via the payload are set on the object. For subsequent synchronization calls, the fields +listed in this setting are ignored and not synchronized. This can be used to allow modifying some +fields manually in Richie regardless of what OpenEdX sends after an initial value is set.

    Note that this setting is only effective for course runs with the sync_mode field set to a +value other then manual.

    • Type: enum(string)
    • Required: No
    • Value: for example ["languages"]

    Technical support

    If you encounter an issue with this documentation or the backends included in Richie, please +open an issue on our repository.

    If you need a custom backend, you can submit a PR or +open an issue and we will consider adding it.

    + + + + \ No newline at end of file diff --git a/docs/2.21.1/lms-connection/index.html b/docs/2.21.1/lms-connection/index.html new file mode 100644 index 0000000000..bac8ff63c6 --- /dev/null +++ b/docs/2.21.1/lms-connection/index.html @@ -0,0 +1,37 @@ + + + + + +Connecting Richie with one or more LMS | Richie + + + + +
    +
    Version: 2.21.1

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated +seamless experience.

    As of today, each approach has been implemented for OpenEdX but the same could be done for +other LMSes like Moodle, at the cost of minor adaptations.

    1. Displaying connection status

    OpenEdX can be configured to allow CORS requests. Doing so allows Richie to retrieve a user's +connection status from OpenEdx and display the user's profile information directly on the Richie +site: username, dashboard url, etc.

    In this approach, a user visiting your Richie site and trying to signup or login, is sent to the +OpenEdX site for authentication and is redirected back to the Richie site upon successful login.

    You can see this in action on https://www.fun-mooc.fr.

    We provide detailed instructions on +how to configure displaying OpenEdX connection status in Richie.

    2. Seamless enrollment

    Thanks to OpenEdX's enrollment API, it is possible to let users enroll on course runs without +leaving Richie.

    You can see this in action on https://www.fun-mooc.fr.

    This feature requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that +are both subdomains of the same root domain, e.g. richie.example.com and lms.example.com.

    You should read our guide on how to use OpenEdX as LMS backend for Richie.

    3. Synchronizing course runs details

    Course runs in Richie can be handled manually, filling all fields via the DjangoCMS front-end +editing interface. But a better way to handle course runs is to synchronize them automatically +from your LMS using the course run synchronization API.

    Please refer to our guide on how to synchronize course runs between Richie and OpenEdx

    4. Joanie, the enrollment manager

    For more advanced use cases, we have started a new project called Joanie which acts as an +enrollment manager for Richie.

    Authentication in Joanie is done via JWT Tokens for maximum flexibility and decoupling in +identity management.

    The project started early 2021, but over time, Joanie will handle:

    • paid enrollments / certification
    • micro-credentials
    • user dashboard
    • cohorts management (academic or B2B)
    • multi-LMS catalogs
    • time based enrollment

    Development

    For development purposes, the docker-compose project provided on +Richie's code repository is pre-configured to connect +with an OpenEdx instance started with +OpenEdx Docker, which provides a ready-to-use +docker-compose stack of OpenEdx in several flavors. Head over to +OpenEdx Docker README for instructions on how to bootstrap an OpenEdX instance.

    Now, start both the OpenEdX and Richie projects separately with make run.

    Richie should respond on http://localhost:8070, OpenEdx on http://localhost:8073 and both +apps should be able to communicate with each other via the network bridge defined in +docker-compose.

    If you want to activate seamless enrollment locally for development, +you will need to set up TLS domains for both Richie and OpenEdX. To do this, head over to our +guide on setting-up TLS connections for Richie and OpenEdX.

    + + + + \ No newline at end of file diff --git a/docs/2.21.1/native-installation/index.html b/docs/2.21.1/native-installation/index.html new file mode 100644 index 0000000000..5891c17005 --- /dev/null +++ b/docs/2.21.1/native-installation/index.html @@ -0,0 +1,40 @@ + + + + + +Installing Richie on your machine | Richie + + + + +
    +
    Version: 2.21.1

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +installation on your laptop.

    A better approach is to use Docker as explained in +our guide for container-native installation instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh +installation.

    If you are using another operating system or distribution, you can use +Vagrant to get a +running Ubuntu 18.04 server in seconds.

    System update

    Be sure to have fresh packages on the server (kernel, libc, ssl patches...): +post

    sudo apt-get -y update
    sudo apt-get -y dist-upgrade

    Database part

    You must first install postgresql.

    // On Linux
    sudo apt-get -y install postgresql

    // On OS X
    brew install postgresql@10
    brew services start postgresql@10
    // don't forget to add your new postgres install to the $PATH

    Postgresql is now running.

    Then you can create the database owner and the database itself, using the +postgres user:

    sudo -u postgres -i // skip this on OS X as the default install will use your local user
    createuser fun -sP

    Note: we created the user as a superuser. This should only be done in dev/test +environments.

    Now, create the database with this user:

    createdb richie -O fun -W
    exit

    Elasticsearch

    Ubuntu

    Download and install the Public Signing Key

    $ wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -

    You may need to install the apt-transport-https package on Debian before +proceeding:

    $ sudo apt-get install apt-transport-https

    Save the repository definition to /etc/apt/sources.list.d/elastic-6.3.1.list:

    $ echo "deb https://artifacts.elastic.co/packages/6.3.1/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-6.3.1.list

    Update repository and install

    $ sudo apt-get update
    $ sudo apt-get install elasticsearch
    $ sudo /etc/init.d/elasticsearch start

    OS X

    $ brew install elasticsearch

    Application part

    Python and other requirements

    We use Python 3.6 which is the one installed by default in Ubuntu 18.04.

    You can install it on OS X using the following commands. Make sure to always run +python3 instead of python and pip3 instead of pip to ensure the correct +version of Python (your homebrew install of 3) is used.

    brew install python3
    brew postinstall python3

    The virtualenv

    Place yourself in the application directory app:

    cd app

    We choose to run our application in a virtual environment.

    For this, we'll install virtualenvwrapper and add an environment:

    pip install virtualenvwrapper

    You can open a new shell to activate the virtualenvwrapper commands, or simply +do:

    source $(which virtualenvwrapper.sh)

    Then create the virtual environment for richie:

    mkvirtualenv richie --no-site-packages --python=python3

    The virtualenv should now be activated and you can install the Python +dependencies for development:

    pip install -e .[dev]

    The "dev.txt" requirement file installs packages specific to a dev environment +and should not be used in production.

    Frontend build

    This project is a hybrid that uses both Django generated pages and frontend JS +code. As such, it includes a frontend build process that comes in two parts: JS +& CSS.

    We need NPM to install the dependencies and run the build, which depends on a +version of Nodejs specified in .nvmrc. See the +repo for instructions on how to install NVM. +To take advantage of .nvmrc, run this in the context of the repository:

    nvm install
    nvm use

    As a prerequisite to running the frontend build for either JS or CSS, you'll +need to install yarn and download +dependencies via:

    yarn install
    • JS build
    npm run build
    • CSS build

    This will compile all our SCSS files into one bundle and put it in the static +folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first +time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at +localhost:8000

    python sandbox/manage.py runserver
    + + + + \ No newline at end of file diff --git a/docs/2.21.1/synchronizing-course-runs/index.html b/docs/2.21.1/synchronizing-course-runs/index.html new file mode 100644 index 0000000000..fce06fa53d --- /dev/null +++ b/docs/2.21.1/synchronizing-course-runs/index.html @@ -0,0 +1,35 @@ + + + + + +Synchronizing course runs between Richie and OpenEdX | Richie + + + + +
    +
    Version: 2.21.1

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the +RICHIE_COURSE_RUN_SYNC_SECRETS setting with one or more secrets:

    RICHIE_COURSE_RUN_SYNC_SECRETS = ["SharedSecret", "OtherSharedSecret"]

    This setting collects several secrets in order to allow rotating them without any downtime. Any +of the secrets listed in this setting can be used to sign your queries.

    Your secret should be shared with the LMS or distant system that needs to synchronize its course +runs with the Richie instance. Richie will try the declared secrets one by one until it finds +one that matches the signature sent by the remote system.

    Configure LMS backends

    You then need to configure the LMS handler via the RICHIE_LMS_BACKENDS setting as explained +in our guide on configuring LMS backends. This is +required if you want Richie to create a new course run automatically and associate it with the +right course when the resource link submitted to the course run synchronization API endpoint is +unknown to Richie.

    Each course run can be set to react differently to a synchronization request, thanks to the +sync_mode field. This field can be set to one of the following values:

    • manual: this course run is ignored by the course runs synchronization script. In this case, +the course run can only be edited manually using the DjangoCMS frontend editing.
    • sync_to_draft: only the draft version of this course run is synchronized. A manual +publication is necessary for the update to be visible on the public site.
    • sync_to_public: the public version of this course run is updated by the synchronization +script. As a results, updates are directly visible on the public site without further +publication by a staff user in Richie.

    A DEFAULT_COURSE_RUN_SYNC_MODE parameter in the +RICHIE_LMS_BACKENDS setting, defines what default value is used for new course runs.

    Make a synchronization query

    You can refer to the documentation of the course run synchronization API for details +on the query expected by this endpoint.

    We also share here our sample code to call this synchronization endpoint from OpenEdX. This code +should run on the post_publish signal emitted by the OpenEdX cms application each time a +course run is modified and published.

    Or you can use the Richie Open edX Synchronization +which is based on the following code sample and also includes the enrollment count.

    Given a COURSE_HOOK setting defined as follows in your OpenEdX instance:

    COURSE_HOOK = {
    "secret": "SharedSecret",
    "url": "https://richie.example.com/api/v1.0/course-runs-sync/",
    }

    The code for the synchronization function in OpenEdX could look like this:

    import hashlib
    import hmac
    import json

    from django.conf import settings

    from microsite_configuration import microsite
    import requests
    from xmodule.modulestore.django import modulestore


    def update_course(course_key, *args, **kwargs):
    """Synchronize an OpenEdX course, identified by its course key, with a Richie instance."""
    course = modulestore().get_course(course_key)
    edxapp_domain = microsite.get_value("site_domain", settings.LMS_BASE)

    data = {
    "resource_link": "https://{:s}/courses/{!s}/info".format(
    edxapp_domain, course_key
    ),
    "start": course.start and course.start.isoformat(),
    "end": course.end and course.end.isoformat(),
    "enrollment_start": course.enrollment_start and course.enrollment_start.isoformat(),
    "enrollment_end": course.enrollment_end and course.enrollment_end.isoformat(),
    "languages": [course.language or settings.LANGUAGE_CODE],
    }

    signature = hmac.new(
    setting.COURSE_HOOK["secret"].encode("utf-8"),
    msg=json.dumps(data).encode("utf-8"),
    digestmod=hashlib.sha256,
    ).hexdigest()

    response = requests.post(
    setting.COURSE_HOOK["url"],
    json=data,
    headers={"Authorization": "SIG-HMAC-SHA256 {:s}".format(signature)},
    )

    Thanks to the signal emitted in OpenEdX, this function can then be triggered each time a course +is modified and published:

    from django.dispatch import receiver
    from xmodule.modulestore.django import SignalHandler


    @receiver(SignalHandler.course_published, dispatch_uid='update_course_on_publish')
    def update_course_on_publish(sender, course_key, **kwargs):
    update_course(course_key)
    + + + + \ No newline at end of file diff --git a/docs/2.21.1/tls-connection/index.html b/docs/2.21.1/tls-connection/index.html new file mode 100644 index 0000000000..7e4a87eab3 --- /dev/null +++ b/docs/2.21.1/tls-connection/index.html @@ -0,0 +1,38 @@ + + + + + +Connecting Richie and OpenEdX over TLS for development | Richie + + + + +
    +
    Version: 2.21.1

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 +and uses a fake LMS backend (base.BaseLMSBackend) as you can see if you check the +RICHIE_LMS_BACKENDS setting in env.d/development.

    This base backend uses session storage to fake enrollments to course runs.

    If you want to test real enrollments to an OpenEdX instance hosted on an external domain, OpenEdX +will need to generate a CORS CSRF Cookie. This cookie is flagged as secure, which implies that +we are not able to use it without SSL connections.

    So if you need to use the OpenEdx API to Create, Update or Delete data from Richie, you have to +enable SSL on Richie and OpenEdx on your development environment, which requires a little bit more +configuration. Below, we explain how to serve OpenEdx and Richie over SSL.

    Run OpenEdx and Richie on sibling domains

    Richie and OpenEdx must be on sibling domains ie domains that both are subdomains of the same +parent domain, because sharing secure Cookies on localhost or unrelated domains is blocked. +To do that, you have to edit your hosts file (.e.g /etc/hosts on a *NIX system) to alias a +domain local.dev with two subdomains richie and edx pointing to localhost:

    # /etc/hosts
    127.0.0.1 richie.local.dev
    127.0.0.1 edx.local.dev

    Once this has been done, the OpenEdx app should respond on http://edx.local.dev:8073 +and Richie should respond on http://richie.local.dev:8070. The Richie application should now be +able to make CORS XHR requests to the OpenEdX application.

    Enable TLS

    If you want to develop with OpenEdx as LMS backend of the Richie application (see the +RICHIE_LMS_BACKENDS setting), you need to enable TLS for your development servers. +Both Richie and OpenEdx use Nginx as reverse proxy which eases the SSL setup.

    1. Install mkcert and its Certificate Authority

    First you will need to install mkcert and its Certificate Authority. +mkcert is a little util to ease local certificate generation.

    a. Install mkcert on your local machine

    b. Install Mkcert Certificate Authority

    mkcert -install

    If you do not want to use mkcert, you can generate CA and certificate with openssl. +You will have to put your certificate and its key in the docker/files/etc/nginx/ssl directory +and respectively name them richie.local.dev.pem and richie.local.dev.key.

    2. On Richie

    Then, to setup the SSL configuration with mkcert, run our helper script:

    $ bin/setup-ssl

    If you do not want to use mkcert, read the instructions above to generate a Richie certificate, +and run the helper script with the --no-cert option:

    bin/setup-ssl --no-cert

    3. On OpenEdx

    In the same way, you also have to enable SSL in OpenEdx, by updating the Nginx configuration. +Read how to enable SSL on OpenEdx.

    Once this has been done, the OpenEdx app should respond on https://edx.local.dev:8073 +and Richie should respond on https://richie.local.dev:8070. The richie application should be able +to share cookies with the OpenEdx application to allow CORS CSRF Protected XHR requests.

    4. Start Richie and OpenEdx over SSL

    Now, the OpenEdx application should respond on https://edx.local.dev:8073, and Richie +on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. The next time you want to use SSL, you can run the following +command on both the Richie and OpenEdX projects:

    $ make run-ssl

    Of course, you can still run apps without ssl by using:

    $ make run
    + + + + \ No newline at end of file diff --git a/docs/2.21.1/web-analytics/index.html b/docs/2.21.1/web-analytics/index.html new file mode 100644 index 0000000000..50c6275f9a --- /dev/null +++ b/docs/2.21.1/web-analytics/index.html @@ -0,0 +1,22 @@ + + + + + +Add web analytics to your site | Richie + + + + +
    +
    Version: 2.21.1

    Add web analytics to your site

    Richie has native support to Google Universal Analytics and Google Tag Manager Web Analytics solutions. +The purpose of this file is to explain how you can enable one of the supported Web Analytics providers +and how you can extend Richie with an alternative solution.

    Google Universal Analytics

    Next, it is described how you can configure the Google Universal Analytics on your Richie site.

    Add the WEB_ANALYTICS setting, with the Google Universal Analytics configuration. From the next example replace TRACKING_ID with your tracking id code.

    {
    'google_universal_analytics': {
    'tracking_id': 'TRACKING_ID',
    }
    }

    The current Google Universal Analytics implementation also includes custom dimensions. Those dimensions permit you to create further analyses on Google Universal Analytics or even use them to create custom reports. +Custom dimensions with a value as example:

    • Organizations codes - UNIV_LISBON | UNIV_PORTO
    • Course code - COURSE_XPTO
    • Course runs titles - Summer edition | Winter edition
    • Course runs resource links - http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/info
    • Page title - Introduction to Programming

    Google Tag

    It is possible to configure the Google Tag, gtag.js, on your Richie site.

    Add the WEB_ANALYTICS setting, with the Google Tag configuration like for example:

    {
    'google_tag': {
    'tracking_id': 'TRACKING_ID',
    }
    }

    And don't forget to replace the TRACKING_ID with your tracking id/code from Google Ads, Google Analytics, or other Google product compatible with the gtag.js.

    The Google Tag is initialized with custom dimensions like the Google Universal Analytics.

    Google Tag Manager

    Next, it is described how you can configure the Google Tag Manager, gtm.js, on your Richie site.

    Add the WEB_ANALYTICS setting, with the Google Tag Manager configuration, for example:

    {
    'google_tag_manager': {
    'tracking_id': 'TRACKING_ID',
    }
    }

    And don't forget to replace the TRACKING_ID with your GTM tracking id/code.

    The current Google Tag Manager implementation also defines a custom dimensions like the Google Universal Analytics.

    If you want to use the Environments feature of the Google Tag Manager, you need to include the environment key with its value on google_tag_manager dict inside the WEB_ANALYTICS setting.

    The environments feature in Google Tag Manager is ideal for organizations that want to preview their container changes in a test environment before those changes are published.

    {
    'google_tag_manager': {
    'tracking_id': 'TRACKING_ID',
    'environment': '&gtm_auth=aaaaaaaaaaaaaaaa&gtm_preview=env-99&gtm_cookies_win=x';
    }
    }

    Multiple Web Analytics at the same time

    It is possible to configure several web analytics solutions at the same time or the same solution with different tracking identifications.

    WEB_ANALYTICS setting example to have both Google Universal Analytics and Google Tag Manager:

    {
    'google_universal_analytics': {
    'tracking_id': 'UA-TRACKING_ID',
    },
    'google_tag_manager': {
    'tracking_id': 'GTM-TRACKING_ID',
    }
    }

    Location of the web analytics javascript

    Each web analytics js code can be put on the footer (default value), to put the Javascript on HTML body footer, or header, to put the Javascript code at the end of the HTML head.

    Update the WEB_ANALYTICS setting, like:

    {
    'google_universal_analytics': {
    'tracking_id': 'UA-TRACKING_ID',
    'location': 'footer,
    },
    }

    Add a new Web Analytics solution

    In this section it's described how you can add support to a different Web Analytics solution.

    • override the richie/web_analytics.html template
    • define the WEB_ANALYTICS setting with a value that represents your solution, eg. my-custom-web-analytics-software
    • define the WEB_ANALYTICS setting with your tracking identification
    • optionally change location with footer (default) or head value
    {
    'my-custom-web-analytics-software': {
    'tracking_id': 'MY_CUSTOM_TRACKING_ID',
    'location': 'footer,
    },
    }
    • Example of a richie/web_analytics.html file customization that prints to the browser console log the dimension keys and values:
    <script type="text/javascript">
    {% for dimension_key, dimension_value_list in WEB_ANALYTICS.DIMENSIONS.items %}
    console.log("dimension: index '{{forloop.counter}}' with key '{{ dimension_key }}' with value '{{ dimension_value_list|join:" | " }}'");
    {% endfor %}
    </script>

    Output:

    dimension: index '1' with key 'organizations_codes' with value 'COMPATIBLE-EVEN-KEELED-UTILIZATION-19 | FOCUSED-NEXT-GENERATION-FUNCTIONALITIES-22 | UNIVERSAL-MODULAR-LOCAL-AREA-NETWORK-23'
    dimension: index '2' with key 'course_code' with value '00017'
    dimension: index '3' with key 'course_runs_titles' with value 'Run 0'
    dimension: index '4' with key 'course_runs_resource_links' with value ''
    dimension: index '5' with key 'page_title' with value 'Business-focused zero-defect application'

    But you can also contribute to Richie by creating a pull request to add support for a different web analytics solution. In this last case, you have to edit directly the richie/web_analytics.html template.

    Example of an override of the richie/web_analytics.html file:

    {% extends "richie/web_analytics.html" %}
    {% block web_analytics_additional_providers %}
    {% if provider == "my_custom_web_analytics_software_provider" %}
    <script type="text/javascript" src="{% static 'myapp/js/custom_web_analytics_software.js' %}">
    <script type="text/javascript">
    // javascript code that startups the custom web analytics software
    </script>
    {% endif %}
    {% endblock web_analytics_additional_providers %}

    The web analytics dimensions are being added to the django context using the WEB_ANALYTICS.DIMENSIONS dictionary. Because each dimension value could have multiple values, then each dictionary value is a list. Web analytics dimensions dictionary keys:

    • organizations_codes
    • course_code
    • course_runs_titles
    • course_runs_resource_links
    • page_title

    Example, if you only need the organization codes on your custom richie/web_analytics.html file:

    <script type="text/javascript">
    console.log("organization codes: '{{ WEB_ANALYTICS.DIMENSIONS.organizations_codes |join:" | " }}");
    </script>

    The frontend code also sends events to the web analytics provider. +Richie sends events when the user is enrolled on a course run. +To support different providers, you need to create a similar file +of src/frontend/js/utils/api/web-analytics/google_universal_analytics.ts and change the src/frontend/js/utils/api/web-analytics/index.ts file to include that newer provider.

    + + + + \ No newline at end of file diff --git a/docs/2.3.0/accessibility-testing/index.html b/docs/2.3.0/accessibility-testing/index.html index 561522ae1a..81906b18d3 100644 --- a/docs/2.3.0/accessibility-testing/index.html +++ b/docs/2.3.0/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.3.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.3.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.3.0/building-the-frontend/index.html b/docs/2.3.0/building-the-frontend/index.html index d3570493b1..b16c6718b2 100644 --- a/docs/2.3.0/building-the-frontend/index.html +++ b/docs/2.3.0/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.3.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.3.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.3.0/contributing-guide/index.html b/docs/2.3.0/contributing-guide/index.html index 06a786143e..8dfdb08887 100644 --- a/docs/2.3.0/contributing-guide/index.html +++ b/docs/2.3.0/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.3.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.3.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.3.0/css-guidelines/index.html b/docs/2.3.0/css-guidelines/index.html index dca7c439fa..6baeafe0a1 100644 --- a/docs/2.3.0/css-guidelines/index.html +++ b/docs/2.3.0/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.3.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.3.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.3.0/discover/index.html b/docs/2.3.0/discover/index.html index f3a820d36f..d053d440f6 100644 --- a/docs/2.3.0/discover/index.html +++ b/docs/2.3.0/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
    -
    Version: 2.3.0

    Getting started with Richie

    If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed +

    Version: 2.3.0

    Getting started with Richie

    If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of 4 services:

    • db: the Postgresql database,
    • elasticsearch: the search engine,
    • app: the actual DjangoCMS project with all our application code,
    • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

    At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -27,7 +27,7 @@ to bootstrap an instance.

    Just start apps with make run.

    Richie should respond on http://localhost:8070 and OpenEdx on http://localhost:8073.

    Advanced - Connecting Richie to OpenEdx

    If you want users to enroll on courses in OpenEdx directly from Richie via API calls, you should read the advanced guide to connect Richie to OpenEdx over TLS.

    - - + + \ No newline at end of file diff --git a/docs/2.3.0/django-react-interop/index.html b/docs/2.3.0/django-react-interop/index.html index 5f53d2498a..edfcbef046 100644 --- a/docs/2.3.0/django-react-interop/index.html +++ b/docs/2.3.0/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.3.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.3.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.3.0/docker-development/index.html b/docs/2.3.0/docker-development/index.html index 251ecf9ed3..f97f704b5a 100644 --- a/docs/2.3.0/docker-development/index.html +++ b/docs/2.3.0/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.3.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.3.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.3.0/frontend-overrides/index.html b/docs/2.3.0/frontend-overrides/index.html index 156b156f97..273897a3e5 100644 --- a/docs/2.3.0/frontend-overrides/index.html +++ b/docs/2.3.0/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.3.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.3.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.3.0/internationalization/index.html b/docs/2.3.0/internationalization/index.html index b0866c09ef..044ed01efe 100644 --- a/docs/2.3.0/internationalization/index.html +++ b/docs/2.3.0/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.3.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.3.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.3.0/lms-connection/index.html b/docs/2.3.0/lms-connection/index.html index e0171d1119..81205f9252 100644 --- a/docs/2.3.0/lms-connection/index.html +++ b/docs/2.3.0/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with an LMS | Richie - - + +
    -
    Version: 2.3.0

    Connecting Richie with an LMS

    richie can be connected to one or more Learning Management Systems (LMS) like OpenEdx, Moodle +

    Version: 2.3.0

    Connecting Richie with an LMS

    richie can be connected to one or more Learning Management Systems (LMS) like OpenEdx, Moodle or Canvas for a seamless experience between browsing the course catalog on richie and following the course itself on the LMS.

    In order to connect richie with a LMS, there is an API bridge to synchronize course information and enrollments.

    API bridge

    The APIHandler utility acts as a proxy that routes queries to the correct LMS backend API, @@ -43,7 +43,7 @@ on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. If you want to use SSL later, just use make run-ssl to run OpenEdx and Richie apps. Of course, you can still run apps without ssl by using make run.

    - - + + \ No newline at end of file diff --git a/docs/2.3.0/native-installation/index.html b/docs/2.3.0/native-installation/index.html index 8a7695279a..c3fb2ffed7 100644 --- a/docs/2.3.0/native-installation/index.html +++ b/docs/2.3.0/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.3.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.3.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.3.1/accessibility-testing/index.html b/docs/2.3.1/accessibility-testing/index.html index d1d6935c7b..a73dd30832 100644 --- a/docs/2.3.1/accessibility-testing/index.html +++ b/docs/2.3.1/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.3.1

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.3.1

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.3.1/building-the-frontend/index.html b/docs/2.3.1/building-the-frontend/index.html index f5667d6f03..ea2994afe1 100644 --- a/docs/2.3.1/building-the-frontend/index.html +++ b/docs/2.3.1/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.3.1

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.3.1

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.3.1/contributing-guide/index.html b/docs/2.3.1/contributing-guide/index.html index 206ce5791d..d25d2e3d04 100644 --- a/docs/2.3.1/contributing-guide/index.html +++ b/docs/2.3.1/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.3.1

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.3.1

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.3.1/css-guidelines/index.html b/docs/2.3.1/css-guidelines/index.html index 077dfeb73c..f1bde0d1a9 100644 --- a/docs/2.3.1/css-guidelines/index.html +++ b/docs/2.3.1/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.3.1

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.3.1

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.3.1/discover/index.html b/docs/2.3.1/discover/index.html index 2c9b2b6950..ceb9e1c569 100644 --- a/docs/2.3.1/discover/index.html +++ b/docs/2.3.1/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
    -
    Version: 2.3.1

    Getting started with Richie

    If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed +

    Version: 2.3.1

    Getting started with Richie

    If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of 4 services:

    • db: the Postgresql database,
    • elasticsearch: the search engine,
    • app: the actual DjangoCMS project with all our application code,
    • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

    At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -27,7 +27,7 @@ to bootstrap an instance.

    Just start apps with make run.

    Richie should respond on http://localhost:8070 and OpenEdx on http://localhost:8073.

    Advanced - Connecting Richie to OpenEdx

    If you want users to enroll on courses in OpenEdx directly from Richie via API calls, you should read the advanced guide to connect Richie to OpenEdx over TLS.

    - - + + \ No newline at end of file diff --git a/docs/2.3.1/django-react-interop/index.html b/docs/2.3.1/django-react-interop/index.html index cf74fa8fe8..a0d23240f0 100644 --- a/docs/2.3.1/django-react-interop/index.html +++ b/docs/2.3.1/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.3.1

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.3.1

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.3.1/docker-development/index.html b/docs/2.3.1/docker-development/index.html index 7998a6f4bb..f4d5760899 100644 --- a/docs/2.3.1/docker-development/index.html +++ b/docs/2.3.1/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.3.1

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.3.1

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.3.1/frontend-overrides/index.html b/docs/2.3.1/frontend-overrides/index.html index 957ee930e0..10fe509257 100644 --- a/docs/2.3.1/frontend-overrides/index.html +++ b/docs/2.3.1/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.3.1

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.3.1

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.3.1/internationalization/index.html b/docs/2.3.1/internationalization/index.html index 8c55e41af0..f89f7171d0 100644 --- a/docs/2.3.1/internationalization/index.html +++ b/docs/2.3.1/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.3.1

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.3.1

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.3.1/lms-connection/index.html b/docs/2.3.1/lms-connection/index.html index e7a00b0726..6727777020 100644 --- a/docs/2.3.1/lms-connection/index.html +++ b/docs/2.3.1/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with an LMS | Richie - - + +
    -
    Version: 2.3.1

    Connecting Richie with an LMS

    richie can be connected to one or more Learning Management Systems (LMS) like OpenEdx, Moodle +

    Version: 2.3.1

    Connecting Richie with an LMS

    richie can be connected to one or more Learning Management Systems (LMS) like OpenEdx, Moodle or Canvas for a seamless experience between browsing the course catalog on richie and following the course itself on the LMS.

    In order to connect richie with a LMS, there is an API bridge to synchronize course information and enrollments.

    API bridge

    The APIHandler utility acts as a proxy that routes queries to the correct LMS backend API, @@ -43,7 +43,7 @@ on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. If you want to use SSL later, just use make run-ssl to run OpenEdx and Richie apps. Of course, you can still run apps without ssl by using make run.

    - - + + \ No newline at end of file diff --git a/docs/2.3.1/native-installation/index.html b/docs/2.3.1/native-installation/index.html index b77600fd93..04ca582b5b 100644 --- a/docs/2.3.1/native-installation/index.html +++ b/docs/2.3.1/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.3.1

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.3.1

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.3.2/accessibility-testing/index.html b/docs/2.3.2/accessibility-testing/index.html index fd047e9692..b0026bb514 100644 --- a/docs/2.3.2/accessibility-testing/index.html +++ b/docs/2.3.2/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.3.2

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.3.2

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.3.2/building-the-frontend/index.html b/docs/2.3.2/building-the-frontend/index.html index 3da2178979..20ab0ab150 100644 --- a/docs/2.3.2/building-the-frontend/index.html +++ b/docs/2.3.2/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.3.2

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.3.2

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.3.2/contributing-guide/index.html b/docs/2.3.2/contributing-guide/index.html index d7bc9336d7..c7dc240593 100644 --- a/docs/2.3.2/contributing-guide/index.html +++ b/docs/2.3.2/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.3.2

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.3.2

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.3.2/css-guidelines/index.html b/docs/2.3.2/css-guidelines/index.html index cacd357128..2993a32d90 100644 --- a/docs/2.3.2/css-guidelines/index.html +++ b/docs/2.3.2/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.3.2

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.3.2

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.3.2/discover/index.html b/docs/2.3.2/discover/index.html index c638c26668..423f6d00ca 100644 --- a/docs/2.3.2/discover/index.html +++ b/docs/2.3.2/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
    -
    Version: 2.3.2

    Getting started with Richie

    If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed +

    Version: 2.3.2

    Getting started with Richie

    If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of 4 services:

    • db: the Postgresql database,
    • elasticsearch: the search engine,
    • app: the actual DjangoCMS project with all our application code,
    • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

    At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -27,7 +27,7 @@ to bootstrap an instance.

    Just start apps with make run.

    Richie should respond on http://localhost:8070 and OpenEdx on http://localhost:8073.

    Advanced - Connecting Richie to OpenEdx

    If you want users to enroll on courses in OpenEdx directly from Richie via API calls, you should read the advanced guide to connect Richie to OpenEdx over TLS.

    - - + + \ No newline at end of file diff --git a/docs/2.3.2/django-react-interop/index.html b/docs/2.3.2/django-react-interop/index.html index d162c16ff4..1d81858bab 100644 --- a/docs/2.3.2/django-react-interop/index.html +++ b/docs/2.3.2/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.3.2

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.3.2

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.3.2/docker-development/index.html b/docs/2.3.2/docker-development/index.html index 2ef6290c6c..fb8aba502b 100644 --- a/docs/2.3.2/docker-development/index.html +++ b/docs/2.3.2/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.3.2

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.3.2

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.3.2/frontend-overrides/index.html b/docs/2.3.2/frontend-overrides/index.html index ec020a4134..8b04a085aa 100644 --- a/docs/2.3.2/frontend-overrides/index.html +++ b/docs/2.3.2/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.3.2

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.3.2

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.3.2/internationalization/index.html b/docs/2.3.2/internationalization/index.html index 1a21975428..f7098bd519 100644 --- a/docs/2.3.2/internationalization/index.html +++ b/docs/2.3.2/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.3.2

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.3.2

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.3.2/lms-connection/index.html b/docs/2.3.2/lms-connection/index.html index 4fd6ad24c0..b187719848 100644 --- a/docs/2.3.2/lms-connection/index.html +++ b/docs/2.3.2/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with an LMS | Richie - - + +
    -
    Version: 2.3.2

    Connecting Richie with an LMS

    richie can be connected to one or more Learning Management Systems (LMS) like OpenEdx, Moodle +

    Version: 2.3.2

    Connecting Richie with an LMS

    richie can be connected to one or more Learning Management Systems (LMS) like OpenEdx, Moodle or Canvas for a seamless experience between browsing the course catalog on richie and following the course itself on the LMS.

    In order to connect richie with a LMS, there is an API bridge to synchronize course information and enrollments.

    API bridge

    The APIHandler utility acts as a proxy that routes queries to the correct LMS backend API, @@ -43,7 +43,7 @@ on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. If you want to use SSL later, just use make run-ssl to run OpenEdx and Richie apps. Of course, you can still run apps without ssl by using make run.

    - - + + \ No newline at end of file diff --git a/docs/2.3.2/native-installation/index.html b/docs/2.3.2/native-installation/index.html index d43e87d9b4..496977b5b2 100644 --- a/docs/2.3.2/native-installation/index.html +++ b/docs/2.3.2/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.3.2

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.3.2

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.3.3/accessibility-testing/index.html b/docs/2.3.3/accessibility-testing/index.html index d0119b4690..8ede89a2bb 100644 --- a/docs/2.3.3/accessibility-testing/index.html +++ b/docs/2.3.3/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.3.3

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.3.3

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.3.3/building-the-frontend/index.html b/docs/2.3.3/building-the-frontend/index.html index b3f3d4329e..028763a2a1 100644 --- a/docs/2.3.3/building-the-frontend/index.html +++ b/docs/2.3.3/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.3.3

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.3.3

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.3.3/contributing-guide/index.html b/docs/2.3.3/contributing-guide/index.html index 2150f7a37e..980add259c 100644 --- a/docs/2.3.3/contributing-guide/index.html +++ b/docs/2.3.3/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.3.3

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.3.3

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.3.3/css-guidelines/index.html b/docs/2.3.3/css-guidelines/index.html index 96cf5c1411..cb2b088c66 100644 --- a/docs/2.3.3/css-guidelines/index.html +++ b/docs/2.3.3/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.3.3

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.3.3

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.3.3/discover/index.html b/docs/2.3.3/discover/index.html index 206744d59a..e0c894089b 100644 --- a/docs/2.3.3/discover/index.html +++ b/docs/2.3.3/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
    -
    Version: 2.3.3

    Getting started with Richie

    If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed +

    Version: 2.3.3

    Getting started with Richie

    If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of 4 services:

    • db: the Postgresql database,
    • elasticsearch: the search engine,
    • app: the actual DjangoCMS project with all our application code,
    • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

    At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -27,7 +27,7 @@ to bootstrap an instance.

    Just start apps with make run.

    Richie should respond on http://localhost:8070 and OpenEdx on http://localhost:8073.

    Advanced - Connecting Richie to OpenEdx

    If you want users to enroll on courses in OpenEdx directly from Richie via API calls, you should read the advanced guide to connect Richie to OpenEdx over TLS.

    - - + + \ No newline at end of file diff --git a/docs/2.3.3/django-react-interop/index.html b/docs/2.3.3/django-react-interop/index.html index 774d55368c..ce8a0c3c7f 100644 --- a/docs/2.3.3/django-react-interop/index.html +++ b/docs/2.3.3/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.3.3

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.3.3

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.3.3/docker-development/index.html b/docs/2.3.3/docker-development/index.html index 4f6a7f9a9f..1057c59e14 100644 --- a/docs/2.3.3/docker-development/index.html +++ b/docs/2.3.3/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.3.3

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.3.3

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.3.3/frontend-overrides/index.html b/docs/2.3.3/frontend-overrides/index.html index f9a84b1214..9e68129062 100644 --- a/docs/2.3.3/frontend-overrides/index.html +++ b/docs/2.3.3/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.3.3

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.3.3

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.3.3/internationalization/index.html b/docs/2.3.3/internationalization/index.html index 5729d2c0a7..5e66e85252 100644 --- a/docs/2.3.3/internationalization/index.html +++ b/docs/2.3.3/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.3.3

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.3.3

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.3.3/lms-connection/index.html b/docs/2.3.3/lms-connection/index.html index edd5f37b07..1b7012d1bf 100644 --- a/docs/2.3.3/lms-connection/index.html +++ b/docs/2.3.3/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with an LMS | Richie - - + +
    -
    Version: 2.3.3

    Connecting Richie with an LMS

    richie can be connected to one or more Learning Management Systems (LMS) like OpenEdx, Moodle +

    Version: 2.3.3

    Connecting Richie with an LMS

    richie can be connected to one or more Learning Management Systems (LMS) like OpenEdx, Moodle or Canvas for a seamless experience between browsing the course catalog on richie and following the course itself on the LMS.

    In order to connect richie with a LMS, there is an API bridge to synchronize course information and enrollments.

    API bridge

    The APIHandler utility acts as a proxy that routes queries to the correct LMS backend API, @@ -43,7 +43,7 @@ on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. If you want to use SSL later, just use make run-ssl to run OpenEdx and Richie apps. Of course, you can still run apps without ssl by using make run.

    - - + + \ No newline at end of file diff --git a/docs/2.3.3/native-installation/index.html b/docs/2.3.3/native-installation/index.html index 40aae8838c..dcfd7342a2 100644 --- a/docs/2.3.3/native-installation/index.html +++ b/docs/2.3.3/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.3.3

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.3.3

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.4.0/accessibility-testing/index.html b/docs/2.4.0/accessibility-testing/index.html index 472580979e..17d7436da3 100644 --- a/docs/2.4.0/accessibility-testing/index.html +++ b/docs/2.4.0/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.4.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.4.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.4.0/building-the-frontend/index.html b/docs/2.4.0/building-the-frontend/index.html index fdad51de55..14cc10db2f 100644 --- a/docs/2.4.0/building-the-frontend/index.html +++ b/docs/2.4.0/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.4.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.4.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.4.0/contributing-guide/index.html b/docs/2.4.0/contributing-guide/index.html index 9c0056e580..e6e61f13ed 100644 --- a/docs/2.4.0/contributing-guide/index.html +++ b/docs/2.4.0/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.4.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.4.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.4.0/css-guidelines/index.html b/docs/2.4.0/css-guidelines/index.html index 501a569e9d..83312e1259 100644 --- a/docs/2.4.0/css-guidelines/index.html +++ b/docs/2.4.0/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.4.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.4.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.4.0/discover/index.html b/docs/2.4.0/discover/index.html index 55df164d9d..19661bc9ba 100644 --- a/docs/2.4.0/discover/index.html +++ b/docs/2.4.0/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
    -
    Version: 2.4.0

    Getting started with Richie

    If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed +

    Version: 2.4.0

    Getting started with Richie

    If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of 4 services:

    • db: the Postgresql database,
    • elasticsearch: the search engine,
    • app: the actual DjangoCMS project with all our application code,
    • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

    At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -27,7 +27,7 @@ to bootstrap an instance.

    Just start apps with make run.

    Richie should respond on http://localhost:8070 and OpenEdx on http://localhost:8073.

    Advanced - Connecting Richie to OpenEdx

    If you want users to enroll on courses in OpenEdx directly from Richie via API calls, you should read the advanced guide to connect Richie to OpenEdx over TLS.

    - - + + \ No newline at end of file diff --git a/docs/2.4.0/django-react-interop/index.html b/docs/2.4.0/django-react-interop/index.html index 6847e49d27..699414840e 100644 --- a/docs/2.4.0/django-react-interop/index.html +++ b/docs/2.4.0/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.4.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.4.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.4.0/docker-development/index.html b/docs/2.4.0/docker-development/index.html index 789c6add06..a784136a1e 100644 --- a/docs/2.4.0/docker-development/index.html +++ b/docs/2.4.0/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.4.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.4.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.4.0/frontend-overrides/index.html b/docs/2.4.0/frontend-overrides/index.html index 6d8dddbcda..7535b41c7f 100644 --- a/docs/2.4.0/frontend-overrides/index.html +++ b/docs/2.4.0/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.4.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.4.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.4.0/internationalization/index.html b/docs/2.4.0/internationalization/index.html index 3acd438015..d43c64590b 100644 --- a/docs/2.4.0/internationalization/index.html +++ b/docs/2.4.0/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.4.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.4.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.4.0/lms-connection/index.html b/docs/2.4.0/lms-connection/index.html index 5f1c864d2c..e960467e07 100644 --- a/docs/2.4.0/lms-connection/index.html +++ b/docs/2.4.0/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with an LMS | Richie - - + +
    -
    Version: 2.4.0

    Connecting Richie with an LMS

    richie can be connected to one or more Learning Management Systems (LMS) like OpenEdx, Moodle +

    Version: 2.4.0

    Connecting Richie with an LMS

    richie can be connected to one or more Learning Management Systems (LMS) like OpenEdx, Moodle or Canvas for a seamless experience between browsing the course catalog on richie and following the course itself on the LMS.

    In order to connect richie with a LMS, there is an API bridge to synchronize course information and enrollments.

    API bridge

    The APIHandler utility acts as a proxy that routes queries to the correct LMS backend API, @@ -43,7 +43,7 @@ on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. If you want to use SSL later, just use make run-ssl to run OpenEdx and Richie apps. Of course, you can still run apps without ssl by using make run.

    - - + + \ No newline at end of file diff --git a/docs/2.4.0/native-installation/index.html b/docs/2.4.0/native-installation/index.html index 849919c612..660f0281f1 100644 --- a/docs/2.4.0/native-installation/index.html +++ b/docs/2.4.0/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.4.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.4.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.5.0/accessibility-testing/index.html b/docs/2.5.0/accessibility-testing/index.html index 3ab9ddbdbe..fed7628ec6 100644 --- a/docs/2.5.0/accessibility-testing/index.html +++ b/docs/2.5.0/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.5.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.5.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.5.0/building-the-frontend/index.html b/docs/2.5.0/building-the-frontend/index.html index 0c01e71672..42f53e5e17 100644 --- a/docs/2.5.0/building-the-frontend/index.html +++ b/docs/2.5.0/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.5.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.5.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.5.0/contributing-guide/index.html b/docs/2.5.0/contributing-guide/index.html index 33ffd494ec..9123c179e8 100644 --- a/docs/2.5.0/contributing-guide/index.html +++ b/docs/2.5.0/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.5.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.5.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.5.0/css-guidelines/index.html b/docs/2.5.0/css-guidelines/index.html index 0511466719..8c89556591 100644 --- a/docs/2.5.0/css-guidelines/index.html +++ b/docs/2.5.0/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.5.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.5.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.5.0/discover/index.html b/docs/2.5.0/discover/index.html index 8b0542adfb..d67a1e3827 100644 --- a/docs/2.5.0/discover/index.html +++ b/docs/2.5.0/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
    -
    Version: 2.5.0

    Getting started with Richie

    If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed +

    Version: 2.5.0

    Getting started with Richie

    If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of 4 services:

    • db: the Postgresql database,
    • elasticsearch: the search engine,
    • app: the actual DjangoCMS project with all our application code,
    • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

    At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -27,7 +27,7 @@ to bootstrap an instance.

    Just start apps with make run.

    Richie should respond on http://localhost:8070 and OpenEdx on http://localhost:8073.

    Advanced - Connecting Richie to OpenEdx

    If you want users to enroll on courses in OpenEdx directly from Richie via API calls, you should read the advanced guide to connect Richie to OpenEdx over TLS.

    - - + + \ No newline at end of file diff --git a/docs/2.5.0/django-react-interop/index.html b/docs/2.5.0/django-react-interop/index.html index b0209be63c..d95a4a09e8 100644 --- a/docs/2.5.0/django-react-interop/index.html +++ b/docs/2.5.0/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.5.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.5.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.5.0/docker-development/index.html b/docs/2.5.0/docker-development/index.html index e98142bf6e..c0c18e5e78 100644 --- a/docs/2.5.0/docker-development/index.html +++ b/docs/2.5.0/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.5.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.5.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.5.0/frontend-overrides/index.html b/docs/2.5.0/frontend-overrides/index.html index 0b7cb0f10c..48551612e8 100644 --- a/docs/2.5.0/frontend-overrides/index.html +++ b/docs/2.5.0/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.5.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.5.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.5.0/internationalization/index.html b/docs/2.5.0/internationalization/index.html index bca0be67ca..4bca29c15f 100644 --- a/docs/2.5.0/internationalization/index.html +++ b/docs/2.5.0/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.5.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.5.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.5.0/lms-connection/index.html b/docs/2.5.0/lms-connection/index.html index 4cae47e20d..65466f8e33 100644 --- a/docs/2.5.0/lms-connection/index.html +++ b/docs/2.5.0/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with an LMS | Richie - - + +
    -
    Version: 2.5.0

    Connecting Richie with an LMS

    richie can be connected to one or more Learning Management Systems (LMS) like OpenEdx, Moodle +

    Version: 2.5.0

    Connecting Richie with an LMS

    richie can be connected to one or more Learning Management Systems (LMS) like OpenEdx, Moodle or Canvas for a seamless experience between browsing the course catalog on richie and following the course itself on the LMS.

    In order to connect richie with a LMS, there is an API bridge to synchronize course information and enrollments.

    API bridge

    The APIHandler utility acts as a proxy that routes queries to the correct LMS backend API, @@ -43,7 +43,7 @@ on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. If you want to use SSL later, just use make run-ssl to run OpenEdx and Richie apps. Of course, you can still run apps without ssl by using make run.

    - - + + \ No newline at end of file diff --git a/docs/2.5.0/native-installation/index.html b/docs/2.5.0/native-installation/index.html index 9e49412150..3cbf29c2d0 100644 --- a/docs/2.5.0/native-installation/index.html +++ b/docs/2.5.0/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.5.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.5.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.6.0/accessibility-testing/index.html b/docs/2.6.0/accessibility-testing/index.html index 0caa94dbbd..96a49ee184 100644 --- a/docs/2.6.0/accessibility-testing/index.html +++ b/docs/2.6.0/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.6.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.6.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.6.0/building-the-frontend/index.html b/docs/2.6.0/building-the-frontend/index.html index a6098819fb..3b13ddc327 100644 --- a/docs/2.6.0/building-the-frontend/index.html +++ b/docs/2.6.0/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.6.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.6.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.6.0/contributing-guide/index.html b/docs/2.6.0/contributing-guide/index.html index a7029f2aa9..c1f8c09276 100644 --- a/docs/2.6.0/contributing-guide/index.html +++ b/docs/2.6.0/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.6.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.6.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.6.0/css-guidelines/index.html b/docs/2.6.0/css-guidelines/index.html index 02fba798c6..b99ec65258 100644 --- a/docs/2.6.0/css-guidelines/index.html +++ b/docs/2.6.0/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.6.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.6.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.6.0/discover/index.html b/docs/2.6.0/discover/index.html index c94fcca195..5df506256e 100644 --- a/docs/2.6.0/discover/index.html +++ b/docs/2.6.0/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
    -
    Version: 2.6.0

    Getting started with Richie

    If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed +

    Version: 2.6.0

    Getting started with Richie

    If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of 4 services:

    • db: the Postgresql database,
    • elasticsearch: the search engine,
    • app: the actual DjangoCMS project with all our application code,
    • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

    At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -27,7 +27,7 @@ to bootstrap an instance.

    Just start apps with make run.

    Richie should respond on http://localhost:8070 and OpenEdx on http://localhost:8073.

    Advanced - Connecting Richie to OpenEdx

    If you want users to enroll on courses in OpenEdx directly from Richie via API calls, you should read the advanced guide to connect Richie to OpenEdx over TLS.

    - - + + \ No newline at end of file diff --git a/docs/2.6.0/django-react-interop/index.html b/docs/2.6.0/django-react-interop/index.html index 1c2086f8f8..9fa190fcba 100644 --- a/docs/2.6.0/django-react-interop/index.html +++ b/docs/2.6.0/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.6.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.6.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.6.0/docker-development/index.html b/docs/2.6.0/docker-development/index.html index ae1371b411..73e08d7b55 100644 --- a/docs/2.6.0/docker-development/index.html +++ b/docs/2.6.0/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.6.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.6.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.6.0/frontend-overrides/index.html b/docs/2.6.0/frontend-overrides/index.html index e2c7dc3ce9..73641450fb 100644 --- a/docs/2.6.0/frontend-overrides/index.html +++ b/docs/2.6.0/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.6.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.6.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.6.0/internationalization/index.html b/docs/2.6.0/internationalization/index.html index 1c7a5ef7fa..828c223e2d 100644 --- a/docs/2.6.0/internationalization/index.html +++ b/docs/2.6.0/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.6.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.6.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.6.0/lms-connection/index.html b/docs/2.6.0/lms-connection/index.html index b6caedf719..d3c2084ae0 100644 --- a/docs/2.6.0/lms-connection/index.html +++ b/docs/2.6.0/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with an LMS | Richie - - + +
    -
    Version: 2.6.0

    Connecting Richie with an LMS

    richie can be connected to one or more Learning Management Systems (LMS) like OpenEdx, Moodle +

    Version: 2.6.0

    Connecting Richie with an LMS

    richie can be connected to one or more Learning Management Systems (LMS) like OpenEdx, Moodle or Canvas for a seamless experience between browsing the course catalog on richie and following the course itself on the LMS.

    In order to connect richie with a LMS, there is an API bridge to synchronize course information and enrollments.

    API bridge

    The APIHandler utility acts as a proxy that routes queries to the correct LMS backend API, @@ -43,7 +43,7 @@ on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. If you want to use SSL later, just use make run-ssl to run OpenEdx and Richie apps. Of course, you can still run apps without ssl by using make run.

    - - + + \ No newline at end of file diff --git a/docs/2.6.0/native-installation/index.html b/docs/2.6.0/native-installation/index.html index 4d348324ab..87fd11183f 100644 --- a/docs/2.6.0/native-installation/index.html +++ b/docs/2.6.0/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.6.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.6.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.7.0/accessibility-testing/index.html b/docs/2.7.0/accessibility-testing/index.html index 46aaa7bb38..0c58ef13c9 100644 --- a/docs/2.7.0/accessibility-testing/index.html +++ b/docs/2.7.0/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.7.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.7.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.7.0/building-the-frontend/index.html b/docs/2.7.0/building-the-frontend/index.html index 5aae5ff7af..99f39fe2f6 100644 --- a/docs/2.7.0/building-the-frontend/index.html +++ b/docs/2.7.0/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.7.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.7.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.7.0/contributing-guide/index.html b/docs/2.7.0/contributing-guide/index.html index bb385ac0a0..215aa8f546 100644 --- a/docs/2.7.0/contributing-guide/index.html +++ b/docs/2.7.0/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.7.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.7.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.7.0/css-guidelines/index.html b/docs/2.7.0/css-guidelines/index.html index f0601f57bf..4bea27be3b 100644 --- a/docs/2.7.0/css-guidelines/index.html +++ b/docs/2.7.0/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.7.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.7.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.7.0/discover/index.html b/docs/2.7.0/discover/index.html index 91aff505ea..c06d939af6 100644 --- a/docs/2.7.0/discover/index.html +++ b/docs/2.7.0/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
    -
    Version: 2.7.0

    Getting started with Richie

    If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed +

    Version: 2.7.0

    Getting started with Richie

    If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of 4 services:

    • db: the Postgresql database,
    • elasticsearch: the search engine,
    • app: the actual DjangoCMS project with all our application code,
    • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

    At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -27,7 +27,7 @@ to bootstrap an instance.

    Just start apps with make run.

    Richie should respond on http://localhost:8070 and OpenEdx on http://localhost:8073.

    Advanced - Connecting Richie to OpenEdx

    If you want users to enroll on courses in OpenEdx directly from Richie via API calls, you should read the advanced guide to connect Richie to OpenEdx over TLS.

    - - + + \ No newline at end of file diff --git a/docs/2.7.0/django-react-interop/index.html b/docs/2.7.0/django-react-interop/index.html index 63f8d55f46..18be73cbd8 100644 --- a/docs/2.7.0/django-react-interop/index.html +++ b/docs/2.7.0/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.7.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.7.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.7.0/docker-development/index.html b/docs/2.7.0/docker-development/index.html index e7ef2d2007..c6243d37b2 100644 --- a/docs/2.7.0/docker-development/index.html +++ b/docs/2.7.0/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.7.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.7.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.7.0/frontend-overrides/index.html b/docs/2.7.0/frontend-overrides/index.html index 4e5871fb94..0e81c093f5 100644 --- a/docs/2.7.0/frontend-overrides/index.html +++ b/docs/2.7.0/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.7.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.7.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.7.0/internationalization/index.html b/docs/2.7.0/internationalization/index.html index b97238e5b5..a6826dbad7 100644 --- a/docs/2.7.0/internationalization/index.html +++ b/docs/2.7.0/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.7.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.7.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.7.0/lms-connection/index.html b/docs/2.7.0/lms-connection/index.html index cad1f864d7..b822af6776 100644 --- a/docs/2.7.0/lms-connection/index.html +++ b/docs/2.7.0/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with an LMS | Richie - - + +
    -
    Version: 2.7.0

    Connecting Richie with an LMS

    richie can be connected to one or more Learning Management Systems (LMS) like OpenEdx, Moodle +

    Version: 2.7.0

    Connecting Richie with an LMS

    richie can be connected to one or more Learning Management Systems (LMS) like OpenEdx, Moodle or Canvas for a seamless experience between browsing the course catalog on richie and following the course itself on the LMS.

    In order to connect richie with a LMS, there is an API bridge to synchronize course information and enrollments.

    API bridge

    The APIHandler utility acts as a proxy that routes queries to the correct LMS backend API, @@ -43,7 +43,7 @@ on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. If you want to use SSL later, just use make run-ssl to run OpenEdx and Richie apps. Of course, you can still run apps without ssl by using make run.

    - - + + \ No newline at end of file diff --git a/docs/2.7.0/native-installation/index.html b/docs/2.7.0/native-installation/index.html index 07d86678d2..d287ad581a 100644 --- a/docs/2.7.0/native-installation/index.html +++ b/docs/2.7.0/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.7.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.7.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.7.1/accessibility-testing/index.html b/docs/2.7.1/accessibility-testing/index.html index 97e65936fe..aa9e870d29 100644 --- a/docs/2.7.1/accessibility-testing/index.html +++ b/docs/2.7.1/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.7.1

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.7.1

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.7.1/building-the-frontend/index.html b/docs/2.7.1/building-the-frontend/index.html index 8fda489c29..3669b49b36 100644 --- a/docs/2.7.1/building-the-frontend/index.html +++ b/docs/2.7.1/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.7.1

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.7.1

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.7.1/contributing-guide/index.html b/docs/2.7.1/contributing-guide/index.html index f4d6328f5f..d2ce061dc0 100644 --- a/docs/2.7.1/contributing-guide/index.html +++ b/docs/2.7.1/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.7.1

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.7.1

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.7.1/css-guidelines/index.html b/docs/2.7.1/css-guidelines/index.html index 1d21afcbdd..68d9d30b9a 100644 --- a/docs/2.7.1/css-guidelines/index.html +++ b/docs/2.7.1/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.7.1

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.7.1

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.7.1/discover/index.html b/docs/2.7.1/discover/index.html index a49184094a..4e024613fb 100644 --- a/docs/2.7.1/discover/index.html +++ b/docs/2.7.1/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
    -
    Version: 2.7.1

    Getting started with Richie

    If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed +

    Version: 2.7.1

    Getting started with Richie

    If you're just looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of 4 services:

    • db: the Postgresql database,
    • elasticsearch: the search engine,
    • app: the actual DjangoCMS project with all our application code,
    • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

    At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -27,7 +27,7 @@ to bootstrap an instance.

    Just start apps with make run.

    Richie should respond on http://localhost:8070 and OpenEdx on http://localhost:8073.

    Advanced - Connecting Richie to OpenEdx

    If you want users to enroll on courses in OpenEdx directly from Richie via API calls, you should read the advanced guide to connect Richie to OpenEdx over TLS.

    - - + + \ No newline at end of file diff --git a/docs/2.7.1/django-react-interop/index.html b/docs/2.7.1/django-react-interop/index.html index ffea36cf61..58afca4188 100644 --- a/docs/2.7.1/django-react-interop/index.html +++ b/docs/2.7.1/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.7.1

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.7.1

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.7.1/docker-development/index.html b/docs/2.7.1/docker-development/index.html index b64f75702b..aa698fca07 100644 --- a/docs/2.7.1/docker-development/index.html +++ b/docs/2.7.1/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.7.1

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.7.1

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.7.1/frontend-overrides/index.html b/docs/2.7.1/frontend-overrides/index.html index 363881d7d4..196fee33f4 100644 --- a/docs/2.7.1/frontend-overrides/index.html +++ b/docs/2.7.1/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.7.1

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.7.1

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.7.1/internationalization/index.html b/docs/2.7.1/internationalization/index.html index 528cbf13fa..1f27cfa725 100644 --- a/docs/2.7.1/internationalization/index.html +++ b/docs/2.7.1/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.7.1

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.7.1

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.7.1/lms-connection/index.html b/docs/2.7.1/lms-connection/index.html index 8b2385cc8e..51351a0994 100644 --- a/docs/2.7.1/lms-connection/index.html +++ b/docs/2.7.1/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with an LMS | Richie - - + +
    -
    Version: 2.7.1

    Connecting Richie with an LMS

    richie can be connected to one or more Learning Management Systems (LMS) like OpenEdx, Moodle +

    Version: 2.7.1

    Connecting Richie with an LMS

    richie can be connected to one or more Learning Management Systems (LMS) like OpenEdx, Moodle or Canvas for a seamless experience between browsing the course catalog on richie and following the course itself on the LMS.

    In order to connect richie with a LMS, there is an API bridge to synchronize course information and enrollments.

    API bridge

    The APIHandler utility acts as a proxy that routes queries to the correct LMS backend API, @@ -43,7 +43,7 @@ on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. If you want to use SSL later, just use make run-ssl to run OpenEdx and Richie apps. Of course, you can still run apps without ssl by using make run.

    - - + + \ No newline at end of file diff --git a/docs/2.7.1/native-installation/index.html b/docs/2.7.1/native-installation/index.html index 637511d4ec..6a851839ed 100644 --- a/docs/2.7.1/native-installation/index.html +++ b/docs/2.7.1/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.7.1

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.7.1

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.8.0/accessibility-testing/index.html b/docs/2.8.0/accessibility-testing/index.html index 9bfc42e777..1e04eb293f 100644 --- a/docs/2.8.0/accessibility-testing/index.html +++ b/docs/2.8.0/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.8.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.8.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.8.0/api/course-run-synchronization-api/index.html b/docs/2.8.0/api/course-run-synchronization-api/index.html index 6a018b0e2b..562f73da97 100644 --- a/docs/2.8.0/api/course-run-synchronization-api/index.html +++ b/docs/2.8.0/api/course-run-synchronization-api/index.html @@ -4,12 +4,12 @@ Course run synchronization API | Richie - - + +
    -
    Version: 2.8.0

    Course run synchronization API

    API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

    Synchronization endpoint [/api/1.0/course-runs-sync]

    This documentation describes version "1.0" of this API endpoint.

    Synchronize a course run [POST]

    It takes a JSON object containing the course run details:

    • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - +
      Version: 2.8.0

      Course run synchronization API

      API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

      Synchronization endpoint [/api/1.0/course-runs-sync]

      This documentation describes version "1.0" of this API endpoint.

      Synchronize a course run [POST]

      It takes a JSON object containing the course run details:

      • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - url of the course syllabus on the LMS from which a unique course identifier can be extracted
      • start: 2018-02-01T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course starts
      • end: 2018-02-28T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course ends
      • enrollment_start: 2018-01-01T06:00:00Z (string, optional) - ISO 8601 date, when enrollment @@ -19,7 +19,7 @@ containing the digest of the utf-8 encoded json representation of the submitted data for the given secret key and SHA256 digest algorithm (see [synchronizing-course-runs] for an example).
    • Body

        {
      "resource_link": "https://lms.example.com/courses/course-v1:001+001+001/info",
      "start": "2021-02-01T00:00:00Z",
      "end": "2021-02-31T23:59:59Z",
      "enrollment_start": "2021-01-01T00:00:00Z",
      "enrollment_end": "2021-01-31T23:59:59Z",
      "languages": ["en", "fr"]
      }
  • Response 200 (application/json)

    • Body

        {
      "success": True
      }
  • - - + + \ No newline at end of file diff --git a/docs/2.8.0/building-the-frontend/index.html b/docs/2.8.0/building-the-frontend/index.html index b24e797ac0..62d5bd575d 100644 --- a/docs/2.8.0/building-the-frontend/index.html +++ b/docs/2.8.0/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.8.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.8.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.8.0/contributing-guide/index.html b/docs/2.8.0/contributing-guide/index.html index 7dbfbd89ec..ca08161ce0 100644 --- a/docs/2.8.0/contributing-guide/index.html +++ b/docs/2.8.0/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.8.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.8.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.8.0/css-guidelines/index.html b/docs/2.8.0/css-guidelines/index.html index c403f782b3..8020ff081b 100644 --- a/docs/2.8.0/css-guidelines/index.html +++ b/docs/2.8.0/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.8.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.8.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.8.0/discover/index.html b/docs/2.8.0/discover/index.html index f9c369ec5a..64da4beac3 100644 --- a/docs/2.8.0/discover/index.html +++ b/docs/2.8.0/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
    -
    Version: 2.8.0

    Getting started with Richie

    If you're looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed +

    Version: 2.8.0

    Getting started with Richie

    If you're looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of 4 services:

    • db: the Postgresql database,
    • elasticsearch: the search engine,
    • app: the actual DjangoCMS project with all our application code,
    • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

    At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -24,7 +24,7 @@ a course on the LMS, and the LMS points back to the catalogue to browse courses.

    This approach is used for example on https://www.fun-campus.fr or https://catalogue.edulib.org.

    For a seamless user experience, it is possible to connect a Richie instance to an OpenEdX instance (or some other LMS like Moodle at the cost of minor adaptations), in several ways that we explain in the LMS connection guide.

    - - + + \ No newline at end of file diff --git a/docs/2.8.0/displaying-connection-status/index.html b/docs/2.8.0/displaying-connection-status/index.html index 80a9b7d692..3ec191d949 100644 --- a/docs/2.8.0/displaying-connection-status/index.html +++ b/docs/2.8.0/displaying-connection-status/index.html @@ -4,12 +4,12 @@ Displaying OpenEdX connection status in Richie | Richie - - + +
    -
    Version: 2.8.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status +

    Version: 2.8.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status and display profile information for the logged-in user in Richie.

    In Richie, the only users that are actually authenticated on the DjangoCMS instance producing the site, are editors and staff users. Your instructors and learners will not have user accounts on Richie, but authentication is delegated to a remote service that can be OpenEdX, KeyCloack, or @@ -26,7 +26,7 @@ set for the above authentication delegation BASE_URL setting.

    If you need to bind user data into a url, wrap the property between brackets. For example, the link configured above for the profile page {base_url:s}/u/(username) would point to https://lms.example.com/u/johndoe for a user carrying the username johndoe.

    - - + + \ No newline at end of file diff --git a/docs/2.8.0/django-react-interop/index.html b/docs/2.8.0/django-react-interop/index.html index bf4ba0e069..b6f88d7b18 100644 --- a/docs/2.8.0/django-react-interop/index.html +++ b/docs/2.8.0/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.8.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.8.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.8.0/docker-development/index.html b/docs/2.8.0/docker-development/index.html index d52ab327f8..ce0bb5ab8c 100644 --- a/docs/2.8.0/docker-development/index.html +++ b/docs/2.8.0/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.8.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.8.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.8.0/frontend-overrides/index.html b/docs/2.8.0/frontend-overrides/index.html index 5de8ca8368..b940a43dc4 100644 --- a/docs/2.8.0/frontend-overrides/index.html +++ b/docs/2.8.0/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.8.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.8.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.8.0/internationalization/index.html b/docs/2.8.0/internationalization/index.html index 94d5831985..0ed5f9855c 100644 --- a/docs/2.8.0/internationalization/index.html +++ b/docs/2.8.0/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.8.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.8.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.8.0/lms-backends/index.html b/docs/2.8.0/lms-backends/index.html index d448894fa9..782c93e2ed 100644 --- a/docs/2.8.0/lms-backends/index.html +++ b/docs/2.8.0/lms-backends/index.html @@ -4,12 +4,12 @@ Configuring LMS Backends | Richie - - + +
    -
    Version: 2.8.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless +

    Version: 2.8.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless experience between browsing the course catalog on Richie, enrolling to a course and following the course itself on the LMS.

    It is possible to do the same with Moodle or any other LMS exposing an enrollment API, at the cost of writing a custom LMS handler backend.

    Prerequisites

    This connection requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that @@ -36,7 +36,7 @@ value other then manual.

    • Type: enum(string)
    • Required: No
    • Value: for example ["languages"]

    Technical support

    If you encounter an issue with this documentation or the backends included in Richie, please open an issue on our repository.

    If you need a custom backend, you can submit a PR or open an issue and we will consider adding it.

    - - + + \ No newline at end of file diff --git a/docs/2.8.0/lms-connection/index.html b/docs/2.8.0/lms-connection/index.html index c9730803f5..fedc66e48a 100644 --- a/docs/2.8.0/lms-connection/index.html +++ b/docs/2.8.0/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with one or more LMS | Richie - - + +
    -
    Version: 2.8.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated +

    Version: 2.8.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated seamless experience.

    As of today, each approach has been implemented for OpenEdX but the same could be done for other LMSes like Moodle, at the cost of minor adaptations.

    1. Displaying connection status

    OpenEdX can be configured to allow CORS requests. Doing so allows Richie to retrieve a user's connection status from OpenEdx and display the user's profile information directly on the Richie @@ -31,7 +31,7 @@ docker-compose.

    If you want to activate seamless enrollment locally for development, you will need to set up TLS domains for both Richie and OpenEdX. To do this, head over to our guide on setting-up TLS connections for Richie and OpenEdX.

    - - + + \ No newline at end of file diff --git a/docs/2.8.0/native-installation/index.html b/docs/2.8.0/native-installation/index.html index 8f188ebd49..18b5c6de8a 100644 --- a/docs/2.8.0/native-installation/index.html +++ b/docs/2.8.0/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.8.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.8.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.8.0/synchronizing-course-runs/index.html b/docs/2.8.0/synchronizing-course-runs/index.html index fd27f21ed8..c2e8f3c158 100644 --- a/docs/2.8.0/synchronizing-course-runs/index.html +++ b/docs/2.8.0/synchronizing-course-runs/index.html @@ -4,12 +4,12 @@ Synchronizing course runs between Richie and OpenEdX | Richie - - + +
    -
    Version: 2.8.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the +

    Version: 2.8.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the RICHIE_COURSE_RUN_SYNC_SECRETS setting with one or more secrets:

    RICHIE_COURSE_RUN_SYNC_SECRETS = ["SharedSecret", "OtherSharedSecret"]

    This setting collects several secrets in order to allow rotating them without any downtime. Any of the secrets listed in this setting can be used to sign your queries.

    Your secret should be shared with the LMS or distant system that needs to synchronize its course runs with the Richie instance. Richie will try the declared secrets one by one until it finds @@ -28,7 +28,7 @@ should run on the post_publish signal emitted by the OpenEdX cms application each time a course run is modified and published.

    Given a COURSE_HOOK setting defined as follows in your OpenEdX instance:

    COURSE_HOOK = {
    "secret": "SharedSecret",
    "url": "https://richie.example.com/api/v1.0/course-runs-sync/",
    }

    The code for the synchronization function in OpenEdX could look like this:

    import hashlib
    import hmac
    import json

    from django.conf import settings

    from microsite_configuration import microsite
    import requests
    from xmodule.modulestore.django import modulestore


    def update_course(course_key, *args, **kwargs):
    """Synchronize an OpenEdX course, identified by its course key, with a Richie instance."""
    course = modulestore().get_course(course_key)
    edxapp_domain = microsite.get_value("site_domain", settings.LMS_BASE)

    data = {
    "resource_link": "https://{:s}/courses/{!s}/info".format(
    edxapp_domain, course_key
    ),
    "start": course.start and course.start.isoformat(),
    "end": course.end and course.end.isoformat(),
    "enrollment_start": course.enrollment_start and course.enrollment_start.isoformat(),
    "enrollment_end": course.enrollment_end and course.enrollment_end.isoformat(),
    "languages": [course.language or settings.LANGUAGE_CODE],
    }

    signature = hmac.new(
    setting.COURSE_HOOK["secret"].encode("utf-8"),
    msg=json.dumps(data).encode("utf-8"),
    digestmod=hashlib.sha256,
    ).hexdigest()

    response = requests.post(
    setting.COURSE_HOOK["url"],
    json=data,
    headers={"Authorization": "SIG-HMAC-SHA256 {:s}".format(signature)},
    )

    Thanks to the signal emitted in OpenEdX, this function can then be triggered each time a course is modified and published:

    from django.dispatch import receiver
    from xmodule.modulestore.django import SignalHandler


    @receiver(SignalHandler.course_published, dispatch_uid='update_course_on_publish')
    def update_course_on_publish(sender, course_key, **kwargs):
    update_course(course_key)
    - - + + \ No newline at end of file diff --git a/docs/2.8.0/tls-connection/index.html b/docs/2.8.0/tls-connection/index.html index be63965a8f..5fe9698ab2 100644 --- a/docs/2.8.0/tls-connection/index.html +++ b/docs/2.8.0/tls-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie and OpenEdX over TLS for development | Richie - - + +
    -
    Version: 2.8.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 +

    Version: 2.8.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 and uses a fake LMS backend (base.BaseLMSBackend) as you can see if you check the RICHIE_LMS_BACKENDS setting in env.d/development.

    This base backend uses session storage to fake enrollments to course runs.

    If you want to test real enrollments to an OpenEdX instance hosted on an external domain, OpenEdX will need to generate a CORS CSRF Cookie. This cookie is flagged as secure, which implies that @@ -32,7 +32,7 @@ to share cookies with the OpenEdx application to allow CORS CSRF Protected XHR requests.

    4. Start Richie and OpenEdx over SSL

    Now, the OpenEdx application should respond on https://edx.local.dev:8073, and Richie on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. The next time you want to use SSL, you can run the following command on both the Richie and OpenEdX projects:

    $ make run-ssl

    Of course, you can still run apps without ssl by using:

    $ make run
    - - + + \ No newline at end of file diff --git a/docs/2.8.0/web-analytics/index.html b/docs/2.8.0/web-analytics/index.html index bf29e22987..d972dd58db 100644 --- a/docs/2.8.0/web-analytics/index.html +++ b/docs/2.8.0/web-analytics/index.html @@ -4,16 +4,16 @@ Add web analytics to your site | Richie - - + +
    -
    Version: 2.8.0

    Add web analytics to your site

    The purpose of this file is to document how you can enable a Web Analytics solution. +

    Version: 2.8.0

    Add web analytics to your site

    The purpose of this file is to document how you can enable a Web Analytics solution. Currently Richie only supports by default the Google Analytics using the Google Tag Manager Javascript. But it is possible with little cost to add support for other web analytics solutions.

    Google Analytics

    Next, it is decribed how you can configure the Google Analytics on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Analytics tracking id code.

    The current Google Analytics implementation also includes custom dimensions. Those dimensions permit you to create further analyses on Google Analytics or even use them to create custom reports. Custom dimensions with a value as example:

    • Organizations codes - UNIV_LISBON | UNIV_PORTO
    • Course code - COURSE_XPTO
    • Course runs titles - Summer edition | Winter edition
    • Course runs resource links - http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/info
    • Page title - Introduction to Programming

    Location of the web analytics javascript

    Use the WEB_ANALYTICS_LOCATION settings to decide where do you want to put the Javascript code. Use head (default value), to put the Javascript on HTML header, or footer, to put the Javascript code to the bottom of the body.

    Add a new Web Analytics solution

    In this section it's described how you can add support to a different Web Analytics solution.

    • override the richie/web_analytics.html template
    • define the WEB_ANALYTICS_ID setting with your tracking identification
    • define the WEB_ANALYTICS_PROVIDER setting with a value that represents your solution, eg. my-custom-web-analytics-software
    • optionally change WEB_ANALYTICS_LOCATION setting with head (default) or footer value
    • Example of a richie/web_analytics.html file customization that prints to the browser console log the dimension keys and values:
    <script type="text/javascript">
    {% for dimension_key, dimension_value_list in WEB_ANALYTICS.DIMENSIONS.items %}
    console.log("dimension: index '{{forloop.counter}}' with key '{{ dimension_key }}' with value '{{ dimension_value_list|join:" | " }}'");
    {% endfor %}
    </script>

    Output:

    dimension: index '1' with key 'organizations_codes' with value 'COMPATIBLE-EVEN-KEELED-UTILIZATION-19 | FOCUSED-NEXT-GENERATION-FUNCTIONALITIES-22 | UNIVERSAL-MODULAR-LOCAL-AREA-NETWORK-23'
    dimension: index '2' with key 'course_code' with value '00017'
    dimension: index '3' with key 'course_runs_titles' with value 'Run 0'
    dimension: index '4' with key 'course_runs_resource_links' with value ''
    dimension: index '5' with key 'page_title' with value 'Business-focused zero-defect application'

    But you can also contribute to Richie by creating a pull request to add support for a different web analytics solution. In this last case, you have to edit directly the richie/web_analytics.html template.

    Example of an override of the richie/web_analytics.html file:

    {% block web_analytics %}
    {% if WEB_ANALYTICS_ID %}
    {% if WEB_ANALYTICS_PROVIDER == "my_custom_web_analytics_software" %}
    <script type="text/javascript" src="{% static 'myapp/js/custom_web_analytics_software.js' %}">
    <script type="text/javascript">
    // javascript code that startups the custom web analytics software
    </script>
    {% endif %}
    {% endif %}
    {% endblock web_analytics %}

    The web analytics dimensions are being added to the django context using the WEB_ANALYTICS.DIMENSIONS dictionary. Because each dimension value could have multiple values, then each dictionary value is a list. Web analytics dimensions dictionary keys:

    • organizations_codes
    • course_code
    • course_runs_titles
    • course_runs_resource_links
    • page_title

    Example, if you only need the organization codes on your custom richie/web_analytics.html file:

    <script type="text/javascript">
    console.log("organization codes: '{{ WEB_ANALYTICS.DIMENSIONS.organizations_codes |join:" | " }}");
    </script>
    - - + + \ No newline at end of file diff --git a/docs/2.8.1/accessibility-testing/index.html b/docs/2.8.1/accessibility-testing/index.html index d6680e4ccf..b1c2187f55 100644 --- a/docs/2.8.1/accessibility-testing/index.html +++ b/docs/2.8.1/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.8.1

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.8.1

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.8.1/api/course-run-synchronization-api/index.html b/docs/2.8.1/api/course-run-synchronization-api/index.html index 35dfc95be5..7c5095bd08 100644 --- a/docs/2.8.1/api/course-run-synchronization-api/index.html +++ b/docs/2.8.1/api/course-run-synchronization-api/index.html @@ -4,12 +4,12 @@ Course run synchronization API | Richie - - + +
    -
    Version: 2.8.1

    Course run synchronization API

    API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

    Synchronization endpoint [/api/1.0/course-runs-sync]

    This documentation describes version "1.0" of this API endpoint.

    Synchronize a course run [POST]

    It takes a JSON object containing the course run details:

    • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - +
      Version: 2.8.1

      Course run synchronization API

      API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

      Synchronization endpoint [/api/1.0/course-runs-sync]

      This documentation describes version "1.0" of this API endpoint.

      Synchronize a course run [POST]

      It takes a JSON object containing the course run details:

      • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - url of the course syllabus on the LMS from which a unique course identifier can be extracted
      • start: 2018-02-01T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course starts
      • end: 2018-02-28T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course ends
      • enrollment_start: 2018-01-01T06:00:00Z (string, optional) - ISO 8601 date, when enrollment @@ -19,7 +19,7 @@ containing the digest of the utf-8 encoded json representation of the submitted data for the given secret key and SHA256 digest algorithm (see [synchronizing-course-runs] for an example).
    • Body

        {
      "resource_link": "https://lms.example.com/courses/course-v1:001+001+001/info",
      "start": "2021-02-01T00:00:00Z",
      "end": "2021-02-31T23:59:59Z",
      "enrollment_start": "2021-01-01T00:00:00Z",
      "enrollment_end": "2021-01-31T23:59:59Z",
      "languages": ["en", "fr"]
      }
  • Response 200 (application/json)

    • Body

        {
      "success": True
      }
  • - - + + \ No newline at end of file diff --git a/docs/2.8.1/building-the-frontend/index.html b/docs/2.8.1/building-the-frontend/index.html index 06e6cd6d78..3ed6ac55b9 100644 --- a/docs/2.8.1/building-the-frontend/index.html +++ b/docs/2.8.1/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.8.1

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.8.1

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.8.1/contributing-guide/index.html b/docs/2.8.1/contributing-guide/index.html index 02caffd072..cc897bfe72 100644 --- a/docs/2.8.1/contributing-guide/index.html +++ b/docs/2.8.1/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.8.1

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.8.1

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.8.1/css-guidelines/index.html b/docs/2.8.1/css-guidelines/index.html index c5ccc76387..c5c212261a 100644 --- a/docs/2.8.1/css-guidelines/index.html +++ b/docs/2.8.1/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.8.1

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.8.1

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.8.1/discover/index.html b/docs/2.8.1/discover/index.html index 561150d680..22af64c9e3 100644 --- a/docs/2.8.1/discover/index.html +++ b/docs/2.8.1/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
    -
    Version: 2.8.1

    Getting started with Richie

    If you're looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed +

    Version: 2.8.1

    Getting started with Richie

    If you're looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of 4 services:

    • db: the Postgresql database,
    • elasticsearch: the search engine,
    • app: the actual DjangoCMS project with all our application code,
    • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

    At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -24,7 +24,7 @@ a course on the LMS, and the LMS points back to the catalogue to browse courses.

    This approach is used for example on https://www.fun-campus.fr or https://catalogue.edulib.org.

    For a seamless user experience, it is possible to connect a Richie instance to an OpenEdX instance (or some other LMS like Moodle at the cost of minor adaptations), in several ways that we explain in the LMS connection guide.

    - - + + \ No newline at end of file diff --git a/docs/2.8.1/displaying-connection-status/index.html b/docs/2.8.1/displaying-connection-status/index.html index bb653bcfd2..95055cbb1f 100644 --- a/docs/2.8.1/displaying-connection-status/index.html +++ b/docs/2.8.1/displaying-connection-status/index.html @@ -4,12 +4,12 @@ Displaying OpenEdX connection status in Richie | Richie - - + +
    -
    Version: 2.8.1

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status +

    Version: 2.8.1

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status and display profile information for the logged-in user in Richie.

    In Richie, the only users that are actually authenticated on the DjangoCMS instance producing the site, are editors and staff users. Your instructors and learners will not have user accounts on Richie, but authentication is delegated to a remote service that can be OpenEdX, KeyCloack, or @@ -26,7 +26,7 @@ set for the above authentication delegation BASE_URL setting.

    If you need to bind user data into a url, wrap the property between brackets. For example, the link configured above for the profile page {base_url:s}/u/(username) would point to https://lms.example.com/u/johndoe for a user carrying the username johndoe.

    - - + + \ No newline at end of file diff --git a/docs/2.8.1/django-react-interop/index.html b/docs/2.8.1/django-react-interop/index.html index dd8fbff623..f891ebb2cd 100644 --- a/docs/2.8.1/django-react-interop/index.html +++ b/docs/2.8.1/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.8.1

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.8.1

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.8.1/docker-development/index.html b/docs/2.8.1/docker-development/index.html index be7f24f08e..686158aadf 100644 --- a/docs/2.8.1/docker-development/index.html +++ b/docs/2.8.1/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.8.1

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.8.1

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.8.1/frontend-overrides/index.html b/docs/2.8.1/frontend-overrides/index.html index 5f5028b805..8b16043587 100644 --- a/docs/2.8.1/frontend-overrides/index.html +++ b/docs/2.8.1/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.8.1

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.8.1

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.8.1/internationalization/index.html b/docs/2.8.1/internationalization/index.html index 4526872509..73777b4abe 100644 --- a/docs/2.8.1/internationalization/index.html +++ b/docs/2.8.1/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.8.1

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.8.1

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.8.1/lms-backends/index.html b/docs/2.8.1/lms-backends/index.html index f5999d35a6..1a9c8d26f0 100644 --- a/docs/2.8.1/lms-backends/index.html +++ b/docs/2.8.1/lms-backends/index.html @@ -4,12 +4,12 @@ Configuring LMS Backends | Richie - - + +
    -
    Version: 2.8.1

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless +

    Version: 2.8.1

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless experience between browsing the course catalog on Richie, enrolling to a course and following the course itself on the LMS.

    It is possible to do the same with Moodle or any other LMS exposing an enrollment API, at the cost of writing a custom LMS handler backend.

    Prerequisites

    This connection requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that @@ -36,7 +36,7 @@ value other then manual.

    • Type: enum(string)
    • Required: No
    • Value: for example ["languages"]

    Technical support

    If you encounter an issue with this documentation or the backends included in Richie, please open an issue on our repository.

    If you need a custom backend, you can submit a PR or open an issue and we will consider adding it.

    - - + + \ No newline at end of file diff --git a/docs/2.8.1/lms-connection/index.html b/docs/2.8.1/lms-connection/index.html index c43a44777c..965d13c31a 100644 --- a/docs/2.8.1/lms-connection/index.html +++ b/docs/2.8.1/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with one or more LMS | Richie - - + +
    -
    Version: 2.8.1

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated +

    Version: 2.8.1

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated seamless experience.

    As of today, each approach has been implemented for OpenEdX but the same could be done for other LMSes like Moodle, at the cost of minor adaptations.

    1. Displaying connection status

    OpenEdX can be configured to allow CORS requests. Doing so allows Richie to retrieve a user's connection status from OpenEdx and display the user's profile information directly on the Richie @@ -31,7 +31,7 @@ docker-compose.

    If you want to activate seamless enrollment locally for development, you will need to set up TLS domains for both Richie and OpenEdX. To do this, head over to our guide on setting-up TLS connections for Richie and OpenEdX.

    - - + + \ No newline at end of file diff --git a/docs/2.8.1/native-installation/index.html b/docs/2.8.1/native-installation/index.html index 351e7985ab..0f989cec42 100644 --- a/docs/2.8.1/native-installation/index.html +++ b/docs/2.8.1/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.8.1

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.8.1

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.8.1/synchronizing-course-runs/index.html b/docs/2.8.1/synchronizing-course-runs/index.html index aa19bac5f6..b134d8233f 100644 --- a/docs/2.8.1/synchronizing-course-runs/index.html +++ b/docs/2.8.1/synchronizing-course-runs/index.html @@ -4,12 +4,12 @@ Synchronizing course runs between Richie and OpenEdX | Richie - - + +
    -
    Version: 2.8.1

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the +

    Version: 2.8.1

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the RICHIE_COURSE_RUN_SYNC_SECRETS setting with one or more secrets:

    RICHIE_COURSE_RUN_SYNC_SECRETS = ["SharedSecret", "OtherSharedSecret"]

    This setting collects several secrets in order to allow rotating them without any downtime. Any of the secrets listed in this setting can be used to sign your queries.

    Your secret should be shared with the LMS or distant system that needs to synchronize its course runs with the Richie instance. Richie will try the declared secrets one by one until it finds @@ -28,7 +28,7 @@ should run on the post_publish signal emitted by the OpenEdX cms application each time a course run is modified and published.

    Given a COURSE_HOOK setting defined as follows in your OpenEdX instance:

    COURSE_HOOK = {
    "secret": "SharedSecret",
    "url": "https://richie.example.com/api/v1.0/course-runs-sync/",
    }

    The code for the synchronization function in OpenEdX could look like this:

    import hashlib
    import hmac
    import json

    from django.conf import settings

    from microsite_configuration import microsite
    import requests
    from xmodule.modulestore.django import modulestore


    def update_course(course_key, *args, **kwargs):
    """Synchronize an OpenEdX course, identified by its course key, with a Richie instance."""
    course = modulestore().get_course(course_key)
    edxapp_domain = microsite.get_value("site_domain", settings.LMS_BASE)

    data = {
    "resource_link": "https://{:s}/courses/{!s}/info".format(
    edxapp_domain, course_key
    ),
    "start": course.start and course.start.isoformat(),
    "end": course.end and course.end.isoformat(),
    "enrollment_start": course.enrollment_start and course.enrollment_start.isoformat(),
    "enrollment_end": course.enrollment_end and course.enrollment_end.isoformat(),
    "languages": [course.language or settings.LANGUAGE_CODE],
    }

    signature = hmac.new(
    setting.COURSE_HOOK["secret"].encode("utf-8"),
    msg=json.dumps(data).encode("utf-8"),
    digestmod=hashlib.sha256,
    ).hexdigest()

    response = requests.post(
    setting.COURSE_HOOK["url"],
    json=data,
    headers={"Authorization": "SIG-HMAC-SHA256 {:s}".format(signature)},
    )

    Thanks to the signal emitted in OpenEdX, this function can then be triggered each time a course is modified and published:

    from django.dispatch import receiver
    from xmodule.modulestore.django import SignalHandler


    @receiver(SignalHandler.course_published, dispatch_uid='update_course_on_publish')
    def update_course_on_publish(sender, course_key, **kwargs):
    update_course(course_key)
    - - + + \ No newline at end of file diff --git a/docs/2.8.1/tls-connection/index.html b/docs/2.8.1/tls-connection/index.html index 7626d54519..94d1709547 100644 --- a/docs/2.8.1/tls-connection/index.html +++ b/docs/2.8.1/tls-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie and OpenEdX over TLS for development | Richie - - + +
    -
    Version: 2.8.1

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 +

    Version: 2.8.1

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 and uses a fake LMS backend (base.BaseLMSBackend) as you can see if you check the RICHIE_LMS_BACKENDS setting in env.d/development.

    This base backend uses session storage to fake enrollments to course runs.

    If you want to test real enrollments to an OpenEdX instance hosted on an external domain, OpenEdX will need to generate a CORS CSRF Cookie. This cookie is flagged as secure, which implies that @@ -32,7 +32,7 @@ to share cookies with the OpenEdx application to allow CORS CSRF Protected XHR requests.

    4. Start Richie and OpenEdx over SSL

    Now, the OpenEdx application should respond on https://edx.local.dev:8073, and Richie on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. The next time you want to use SSL, you can run the following command on both the Richie and OpenEdX projects:

    $ make run-ssl

    Of course, you can still run apps without ssl by using:

    $ make run
    - - + + \ No newline at end of file diff --git a/docs/2.8.1/web-analytics/index.html b/docs/2.8.1/web-analytics/index.html index 4758ffbebd..238108181d 100644 --- a/docs/2.8.1/web-analytics/index.html +++ b/docs/2.8.1/web-analytics/index.html @@ -4,16 +4,16 @@ Add web analytics to your site | Richie - - + +
    -
    Version: 2.8.1

    Add web analytics to your site

    The purpose of this file is to document how you can enable a Web Analytics solution. +

    Version: 2.8.1

    Add web analytics to your site

    The purpose of this file is to document how you can enable a Web Analytics solution. Currently Richie only supports by default the Google Analytics using the Google Tag Manager Javascript. But it is possible with little cost to add support for other web analytics solutions.

    Google Analytics

    Next, it is decribed how you can configure the Google Analytics on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Analytics tracking id code.

    The current Google Analytics implementation also includes custom dimensions. Those dimensions permit you to create further analyses on Google Analytics or even use them to create custom reports. Custom dimensions with a value as example:

    • Organizations codes - UNIV_LISBON | UNIV_PORTO
    • Course code - COURSE_XPTO
    • Course runs titles - Summer edition | Winter edition
    • Course runs resource links - http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/info
    • Page title - Introduction to Programming

    Location of the web analytics javascript

    Use the WEB_ANALYTICS_LOCATION settings to decide where do you want to put the Javascript code. Use head (default value), to put the Javascript on HTML header, or footer, to put the Javascript code to the bottom of the body.

    Add a new Web Analytics solution

    In this section it's described how you can add support to a different Web Analytics solution.

    • override the richie/web_analytics.html template
    • define the WEB_ANALYTICS_ID setting with your tracking identification
    • define the WEB_ANALYTICS_PROVIDER setting with a value that represents your solution, eg. my-custom-web-analytics-software
    • optionally change WEB_ANALYTICS_LOCATION setting with head (default) or footer value
    • Example of a richie/web_analytics.html file customization that prints to the browser console log the dimension keys and values:
    <script type="text/javascript">
    {% for dimension_key, dimension_value_list in WEB_ANALYTICS.DIMENSIONS.items %}
    console.log("dimension: index '{{forloop.counter}}' with key '{{ dimension_key }}' with value '{{ dimension_value_list|join:" | " }}'");
    {% endfor %}
    </script>

    Output:

    dimension: index '1' with key 'organizations_codes' with value 'COMPATIBLE-EVEN-KEELED-UTILIZATION-19 | FOCUSED-NEXT-GENERATION-FUNCTIONALITIES-22 | UNIVERSAL-MODULAR-LOCAL-AREA-NETWORK-23'
    dimension: index '2' with key 'course_code' with value '00017'
    dimension: index '3' with key 'course_runs_titles' with value 'Run 0'
    dimension: index '4' with key 'course_runs_resource_links' with value ''
    dimension: index '5' with key 'page_title' with value 'Business-focused zero-defect application'

    But you can also contribute to Richie by creating a pull request to add support for a different web analytics solution. In this last case, you have to edit directly the richie/web_analytics.html template.

    Example of an override of the richie/web_analytics.html file:

    {% block web_analytics %}
    {% if WEB_ANALYTICS_ID %}
    {% if WEB_ANALYTICS_PROVIDER == "my_custom_web_analytics_software" %}
    <script type="text/javascript" src="{% static 'myapp/js/custom_web_analytics_software.js' %}">
    <script type="text/javascript">
    // javascript code that startups the custom web analytics software
    </script>
    {% endif %}
    {% endif %}
    {% endblock web_analytics %}

    The web analytics dimensions are being added to the django context using the WEB_ANALYTICS.DIMENSIONS dictionary. Because each dimension value could have multiple values, then each dictionary value is a list. Web analytics dimensions dictionary keys:

    • organizations_codes
    • course_code
    • course_runs_titles
    • course_runs_resource_links
    • page_title

    Example, if you only need the organization codes on your custom richie/web_analytics.html file:

    <script type="text/javascript">
    console.log("organization codes: '{{ WEB_ANALYTICS.DIMENSIONS.organizations_codes |join:" | " }}");
    </script>
    - - + + \ No newline at end of file diff --git a/docs/2.8.2/accessibility-testing/index.html b/docs/2.8.2/accessibility-testing/index.html index b9ab80d8af..b45f7dd8a6 100644 --- a/docs/2.8.2/accessibility-testing/index.html +++ b/docs/2.8.2/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.8.2

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.8.2

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.8.2/api/course-run-synchronization-api/index.html b/docs/2.8.2/api/course-run-synchronization-api/index.html index 05f5e7b595..c6c05721ab 100644 --- a/docs/2.8.2/api/course-run-synchronization-api/index.html +++ b/docs/2.8.2/api/course-run-synchronization-api/index.html @@ -4,12 +4,12 @@ Course run synchronization API | Richie - - + +
    -
    Version: 2.8.2

    Course run synchronization API

    API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

    Synchronization endpoint [/api/1.0/course-runs-sync]

    This documentation describes version "1.0" of this API endpoint.

    Synchronize a course run [POST]

    It takes a JSON object containing the course run details:

    • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - +
      Version: 2.8.2

      Course run synchronization API

      API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

      Synchronization endpoint [/api/1.0/course-runs-sync]

      This documentation describes version "1.0" of this API endpoint.

      Synchronize a course run [POST]

      It takes a JSON object containing the course run details:

      • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - url of the course syllabus on the LMS from which a unique course identifier can be extracted
      • start: 2018-02-01T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course starts
      • end: 2018-02-28T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course ends
      • enrollment_start: 2018-01-01T06:00:00Z (string, optional) - ISO 8601 date, when enrollment @@ -19,7 +19,7 @@ containing the digest of the utf-8 encoded json representation of the submitted data for the given secret key and SHA256 digest algorithm (see [synchronizing-course-runs] for an example).
    • Body

        {
      "resource_link": "https://lms.example.com/courses/course-v1:001+001+001/info",
      "start": "2021-02-01T00:00:00Z",
      "end": "2021-02-31T23:59:59Z",
      "enrollment_start": "2021-01-01T00:00:00Z",
      "enrollment_end": "2021-01-31T23:59:59Z",
      "languages": ["en", "fr"]
      }
  • Response 200 (application/json)

    • Body

        {
      "success": True
      }
  • - - + + \ No newline at end of file diff --git a/docs/2.8.2/building-the-frontend/index.html b/docs/2.8.2/building-the-frontend/index.html index 8f44441fb2..d67cb70e7e 100644 --- a/docs/2.8.2/building-the-frontend/index.html +++ b/docs/2.8.2/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.8.2

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.8.2

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.8.2/contributing-guide/index.html b/docs/2.8.2/contributing-guide/index.html index e6ad9e27c5..8822094653 100644 --- a/docs/2.8.2/contributing-guide/index.html +++ b/docs/2.8.2/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.8.2

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.8.2

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.8.2/css-guidelines/index.html b/docs/2.8.2/css-guidelines/index.html index 7e23a2fb0a..602e86d998 100644 --- a/docs/2.8.2/css-guidelines/index.html +++ b/docs/2.8.2/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.8.2

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.8.2

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.8.2/discover/index.html b/docs/2.8.2/discover/index.html index abde686101..1b9d4928f1 100644 --- a/docs/2.8.2/discover/index.html +++ b/docs/2.8.2/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
    -
    Version: 2.8.2

    Getting started with Richie

    If you're looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed +

    Version: 2.8.2

    Getting started with Richie

    If you're looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of 4 services:

    • db: the Postgresql database,
    • elasticsearch: the search engine,
    • app: the actual DjangoCMS project with all our application code,
    • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

    At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -24,7 +24,7 @@ a course on the LMS, and the LMS points back to the catalogue to browse courses.

    This approach is used for example on https://www.fun-campus.fr or https://catalogue.edulib.org.

    For a seamless user experience, it is possible to connect a Richie instance to an OpenEdX instance (or some other LMS like Moodle at the cost of minor adaptations), in several ways that we explain in the LMS connection guide.

    - - + + \ No newline at end of file diff --git a/docs/2.8.2/displaying-connection-status/index.html b/docs/2.8.2/displaying-connection-status/index.html index 44c6a07035..d66564cd49 100644 --- a/docs/2.8.2/displaying-connection-status/index.html +++ b/docs/2.8.2/displaying-connection-status/index.html @@ -4,12 +4,12 @@ Displaying OpenEdX connection status in Richie | Richie - - + +
    -
    Version: 2.8.2

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status +

    Version: 2.8.2

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status and display profile information for the logged-in user in Richie.

    In Richie, the only users that are actually authenticated on the DjangoCMS instance producing the site, are editors and staff users. Your instructors and learners will not have user accounts on Richie, but authentication is delegated to a remote service that can be OpenEdX, KeyCloack, or @@ -26,7 +26,7 @@ set for the above authentication delegation BASE_URL setting.

    If you need to bind user data into a url, wrap the property between brackets. For example, the link configured above for the profile page {base_url:s}/u/(username) would point to https://lms.example.com/u/johndoe for a user carrying the username johndoe.

    - - + + \ No newline at end of file diff --git a/docs/2.8.2/django-react-interop/index.html b/docs/2.8.2/django-react-interop/index.html index 3dd0fc197f..beb2606218 100644 --- a/docs/2.8.2/django-react-interop/index.html +++ b/docs/2.8.2/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.8.2

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.8.2

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.8.2/docker-development/index.html b/docs/2.8.2/docker-development/index.html index 2789dc5a38..18fb47ff41 100644 --- a/docs/2.8.2/docker-development/index.html +++ b/docs/2.8.2/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.8.2

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.8.2

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.8.2/frontend-overrides/index.html b/docs/2.8.2/frontend-overrides/index.html index 386b957fe1..4cfe05fcd6 100644 --- a/docs/2.8.2/frontend-overrides/index.html +++ b/docs/2.8.2/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.8.2

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.8.2

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.8.2/internationalization/index.html b/docs/2.8.2/internationalization/index.html index aa3c3d12b1..f0c9b43f08 100644 --- a/docs/2.8.2/internationalization/index.html +++ b/docs/2.8.2/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.8.2

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.8.2

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.8.2/lms-backends/index.html b/docs/2.8.2/lms-backends/index.html index a643e7bef5..035baf1c9e 100644 --- a/docs/2.8.2/lms-backends/index.html +++ b/docs/2.8.2/lms-backends/index.html @@ -4,12 +4,12 @@ Configuring LMS Backends | Richie - - + +
    -
    Version: 2.8.2

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless +

    Version: 2.8.2

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless experience between browsing the course catalog on Richie, enrolling to a course and following the course itself on the LMS.

    It is possible to do the same with Moodle or any other LMS exposing an enrollment API, at the cost of writing a custom LMS handler backend.

    Prerequisites

    This connection requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that @@ -36,7 +36,7 @@ value other then manual.

    • Type: enum(string)
    • Required: No
    • Value: for example ["languages"]

    Technical support

    If you encounter an issue with this documentation or the backends included in Richie, please open an issue on our repository.

    If you need a custom backend, you can submit a PR or open an issue and we will consider adding it.

    - - + + \ No newline at end of file diff --git a/docs/2.8.2/lms-connection/index.html b/docs/2.8.2/lms-connection/index.html index 46e58ed2b2..9fc4aa5c1f 100644 --- a/docs/2.8.2/lms-connection/index.html +++ b/docs/2.8.2/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with one or more LMS | Richie - - + +
    -
    Version: 2.8.2

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated +

    Version: 2.8.2

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated seamless experience.

    As of today, each approach has been implemented for OpenEdX but the same could be done for other LMSes like Moodle, at the cost of minor adaptations.

    1. Displaying connection status

    OpenEdX can be configured to allow CORS requests. Doing so allows Richie to retrieve a user's connection status from OpenEdx and display the user's profile information directly on the Richie @@ -31,7 +31,7 @@ docker-compose.

    If you want to activate seamless enrollment locally for development, you will need to set up TLS domains for both Richie and OpenEdX. To do this, head over to our guide on setting-up TLS connections for Richie and OpenEdX.

    - - + + \ No newline at end of file diff --git a/docs/2.8.2/native-installation/index.html b/docs/2.8.2/native-installation/index.html index 4ef2de8d04..03ed31db8f 100644 --- a/docs/2.8.2/native-installation/index.html +++ b/docs/2.8.2/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.8.2

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.8.2

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.8.2/synchronizing-course-runs/index.html b/docs/2.8.2/synchronizing-course-runs/index.html index 14e8c4a3ef..0c314d2117 100644 --- a/docs/2.8.2/synchronizing-course-runs/index.html +++ b/docs/2.8.2/synchronizing-course-runs/index.html @@ -4,12 +4,12 @@ Synchronizing course runs between Richie and OpenEdX | Richie - - + +
    -
    Version: 2.8.2

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the +

    Version: 2.8.2

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the RICHIE_COURSE_RUN_SYNC_SECRETS setting with one or more secrets:

    RICHIE_COURSE_RUN_SYNC_SECRETS = ["SharedSecret", "OtherSharedSecret"]

    This setting collects several secrets in order to allow rotating them without any downtime. Any of the secrets listed in this setting can be used to sign your queries.

    Your secret should be shared with the LMS or distant system that needs to synchronize its course runs with the Richie instance. Richie will try the declared secrets one by one until it finds @@ -28,7 +28,7 @@ should run on the post_publish signal emitted by the OpenEdX cms application each time a course run is modified and published.

    Given a COURSE_HOOK setting defined as follows in your OpenEdX instance:

    COURSE_HOOK = {
    "secret": "SharedSecret",
    "url": "https://richie.example.com/api/v1.0/course-runs-sync/",
    }

    The code for the synchronization function in OpenEdX could look like this:

    import hashlib
    import hmac
    import json

    from django.conf import settings

    from microsite_configuration import microsite
    import requests
    from xmodule.modulestore.django import modulestore


    def update_course(course_key, *args, **kwargs):
    """Synchronize an OpenEdX course, identified by its course key, with a Richie instance."""
    course = modulestore().get_course(course_key)
    edxapp_domain = microsite.get_value("site_domain", settings.LMS_BASE)

    data = {
    "resource_link": "https://{:s}/courses/{!s}/info".format(
    edxapp_domain, course_key
    ),
    "start": course.start and course.start.isoformat(),
    "end": course.end and course.end.isoformat(),
    "enrollment_start": course.enrollment_start and course.enrollment_start.isoformat(),
    "enrollment_end": course.enrollment_end and course.enrollment_end.isoformat(),
    "languages": [course.language or settings.LANGUAGE_CODE],
    }

    signature = hmac.new(
    setting.COURSE_HOOK["secret"].encode("utf-8"),
    msg=json.dumps(data).encode("utf-8"),
    digestmod=hashlib.sha256,
    ).hexdigest()

    response = requests.post(
    setting.COURSE_HOOK["url"],
    json=data,
    headers={"Authorization": "SIG-HMAC-SHA256 {:s}".format(signature)},
    )

    Thanks to the signal emitted in OpenEdX, this function can then be triggered each time a course is modified and published:

    from django.dispatch import receiver
    from xmodule.modulestore.django import SignalHandler


    @receiver(SignalHandler.course_published, dispatch_uid='update_course_on_publish')
    def update_course_on_publish(sender, course_key, **kwargs):
    update_course(course_key)
    - - + + \ No newline at end of file diff --git a/docs/2.8.2/tls-connection/index.html b/docs/2.8.2/tls-connection/index.html index aff7ebc921..493eed16f3 100644 --- a/docs/2.8.2/tls-connection/index.html +++ b/docs/2.8.2/tls-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie and OpenEdX over TLS for development | Richie - - + +
    -
    Version: 2.8.2

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 +

    Version: 2.8.2

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 and uses a fake LMS backend (base.BaseLMSBackend) as you can see if you check the RICHIE_LMS_BACKENDS setting in env.d/development.

    This base backend uses session storage to fake enrollments to course runs.

    If you want to test real enrollments to an OpenEdX instance hosted on an external domain, OpenEdX will need to generate a CORS CSRF Cookie. This cookie is flagged as secure, which implies that @@ -32,7 +32,7 @@ to share cookies with the OpenEdx application to allow CORS CSRF Protected XHR requests.

    4. Start Richie and OpenEdx over SSL

    Now, the OpenEdx application should respond on https://edx.local.dev:8073, and Richie on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. The next time you want to use SSL, you can run the following command on both the Richie and OpenEdX projects:

    $ make run-ssl

    Of course, you can still run apps without ssl by using:

    $ make run
    - - + + \ No newline at end of file diff --git a/docs/2.8.2/web-analytics/index.html b/docs/2.8.2/web-analytics/index.html index e990c936e6..e4e7dd029c 100644 --- a/docs/2.8.2/web-analytics/index.html +++ b/docs/2.8.2/web-analytics/index.html @@ -4,16 +4,16 @@ Add web analytics to your site | Richie - - + +
    -
    Version: 2.8.2

    Add web analytics to your site

    The purpose of this file is to document how you can enable a Web Analytics solution. +

    Version: 2.8.2

    Add web analytics to your site

    The purpose of this file is to document how you can enable a Web Analytics solution. Currently Richie only supports by default the Google Analytics using the Google Tag Manager Javascript. But it is possible with little cost to add support for other web analytics solutions.

    Google Analytics

    Next, it is decribed how you can configure the Google Analytics on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Analytics tracking id code.

    The current Google Analytics implementation also includes custom dimensions. Those dimensions permit you to create further analyses on Google Analytics or even use them to create custom reports. Custom dimensions with a value as example:

    • Organizations codes - UNIV_LISBON | UNIV_PORTO
    • Course code - COURSE_XPTO
    • Course runs titles - Summer edition | Winter edition
    • Course runs resource links - http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/info
    • Page title - Introduction to Programming

    Location of the web analytics javascript

    Use the WEB_ANALYTICS_LOCATION settings to decide where do you want to put the Javascript code. Use head (default value), to put the Javascript on HTML header, or footer, to put the Javascript code to the bottom of the body.

    Add a new Web Analytics solution

    In this section it's described how you can add support to a different Web Analytics solution.

    • override the richie/web_analytics.html template
    • define the WEB_ANALYTICS_ID setting with your tracking identification
    • define the WEB_ANALYTICS_PROVIDER setting with a value that represents your solution, eg. my-custom-web-analytics-software
    • optionally change WEB_ANALYTICS_LOCATION setting with head (default) or footer value
    • Example of a richie/web_analytics.html file customization that prints to the browser console log the dimension keys and values:
    <script type="text/javascript">
    {% for dimension_key, dimension_value_list in WEB_ANALYTICS.DIMENSIONS.items %}
    console.log("dimension: index '{{forloop.counter}}' with key '{{ dimension_key }}' with value '{{ dimension_value_list|join:" | " }}'");
    {% endfor %}
    </script>

    Output:

    dimension: index '1' with key 'organizations_codes' with value 'COMPATIBLE-EVEN-KEELED-UTILIZATION-19 | FOCUSED-NEXT-GENERATION-FUNCTIONALITIES-22 | UNIVERSAL-MODULAR-LOCAL-AREA-NETWORK-23'
    dimension: index '2' with key 'course_code' with value '00017'
    dimension: index '3' with key 'course_runs_titles' with value 'Run 0'
    dimension: index '4' with key 'course_runs_resource_links' with value ''
    dimension: index '5' with key 'page_title' with value 'Business-focused zero-defect application'

    But you can also contribute to Richie by creating a pull request to add support for a different web analytics solution. In this last case, you have to edit directly the richie/web_analytics.html template.

    Example of an override of the richie/web_analytics.html file:

    {% block web_analytics %}
    {% if WEB_ANALYTICS_ID %}
    {% if WEB_ANALYTICS_PROVIDER == "my_custom_web_analytics_software" %}
    <script type="text/javascript" src="{% static 'myapp/js/custom_web_analytics_software.js' %}">
    <script type="text/javascript">
    // javascript code that startups the custom web analytics software
    </script>
    {% endif %}
    {% endif %}
    {% endblock web_analytics %}

    The web analytics dimensions are being added to the django context using the WEB_ANALYTICS.DIMENSIONS dictionary. Because each dimension value could have multiple values, then each dictionary value is a list. Web analytics dimensions dictionary keys:

    • organizations_codes
    • course_code
    • course_runs_titles
    • course_runs_resource_links
    • page_title

    Example, if you only need the organization codes on your custom richie/web_analytics.html file:

    <script type="text/javascript">
    console.log("organization codes: '{{ WEB_ANALYTICS.DIMENSIONS.organizations_codes |join:" | " }}");
    </script>
    - - + + \ No newline at end of file diff --git a/docs/2.9.0/accessibility-testing/index.html b/docs/2.9.0/accessibility-testing/index.html index 831149d81c..c5a8ee204e 100644 --- a/docs/2.9.0/accessibility-testing/index.html +++ b/docs/2.9.0/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.9.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.9.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.9.0/api/course-run-synchronization-api/index.html b/docs/2.9.0/api/course-run-synchronization-api/index.html index beb9da9aed..ab088efae9 100644 --- a/docs/2.9.0/api/course-run-synchronization-api/index.html +++ b/docs/2.9.0/api/course-run-synchronization-api/index.html @@ -4,12 +4,12 @@ Course run synchronization API | Richie - - + +
    -
    Version: 2.9.0

    Course run synchronization API

    API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

    Synchronization endpoint [/api/1.0/course-runs-sync]

    This documentation describes version "1.0" of this API endpoint.

    Synchronize a course run [POST]

    It takes a JSON object containing the course run details:

    • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - +
      Version: 2.9.0

      Course run synchronization API

      API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

      Synchronization endpoint [/api/1.0/course-runs-sync]

      This documentation describes version "1.0" of this API endpoint.

      Synchronize a course run [POST]

      It takes a JSON object containing the course run details:

      • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - url of the course syllabus on the LMS from which a unique course identifier can be extracted
      • start: 2018-02-01T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course starts
      • end: 2018-02-28T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course ends
      • enrollment_start: 2018-01-01T06:00:00Z (string, optional) - ISO 8601 date, when enrollment @@ -19,7 +19,7 @@ containing the digest of the utf-8 encoded json representation of the submitted data for the given secret key and SHA256 digest algorithm (see [synchronizing-course-runs] for an example).
    • Body

        {
      "resource_link": "https://lms.example.com/courses/course-v1:001+001+001/info",
      "start": "2021-02-01T00:00:00Z",
      "end": "2021-02-31T23:59:59Z",
      "enrollment_start": "2021-01-01T00:00:00Z",
      "enrollment_end": "2021-01-31T23:59:59Z",
      "languages": ["en", "fr"]
      }
  • Response 200 (application/json)

    • Body

        {
      "success": True
      }
  • - - + + \ No newline at end of file diff --git a/docs/2.9.0/building-the-frontend/index.html b/docs/2.9.0/building-the-frontend/index.html index d4e9198f98..82600d0fc1 100644 --- a/docs/2.9.0/building-the-frontend/index.html +++ b/docs/2.9.0/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.9.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.9.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.9.0/contributing-guide/index.html b/docs/2.9.0/contributing-guide/index.html index 42929b9b65..e8d33db6c5 100644 --- a/docs/2.9.0/contributing-guide/index.html +++ b/docs/2.9.0/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.9.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.9.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.9.0/css-guidelines/index.html b/docs/2.9.0/css-guidelines/index.html index 3c1289f9d4..2924403199 100644 --- a/docs/2.9.0/css-guidelines/index.html +++ b/docs/2.9.0/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.9.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.9.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.9.0/discover/index.html b/docs/2.9.0/discover/index.html index ba5610fe5d..b84583fb5b 100644 --- a/docs/2.9.0/discover/index.html +++ b/docs/2.9.0/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
    -
    Version: 2.9.0

    Getting started with Richie

    If you're looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed +

    Version: 2.9.0

    Getting started with Richie

    If you're looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of 4 services:

    • db: the Postgresql database,
    • elasticsearch: the search engine,
    • app: the actual DjangoCMS project with all our application code,
    • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

    At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -24,7 +24,7 @@ a course on the LMS, and the LMS points back to the catalogue to browse courses.

    This approach is used for example on https://www.fun-campus.fr or https://catalogue.edulib.org.

    For a seamless user experience, it is possible to connect a Richie instance to an OpenEdX instance (or some other LMS like Moodle at the cost of minor adaptations), in several ways that we explain in the LMS connection guide.

    - - + + \ No newline at end of file diff --git a/docs/2.9.0/displaying-connection-status/index.html b/docs/2.9.0/displaying-connection-status/index.html index 8d5cf358c3..c9f75693a0 100644 --- a/docs/2.9.0/displaying-connection-status/index.html +++ b/docs/2.9.0/displaying-connection-status/index.html @@ -4,12 +4,12 @@ Displaying OpenEdX connection status in Richie | Richie - - + +
    -
    Version: 2.9.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status +

    Version: 2.9.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status and display profile information for the logged-in user in Richie.

    In Richie, the only users that are actually authenticated on the DjangoCMS instance producing the site, are editors and staff users. Your instructors and learners will not have user accounts on Richie, but authentication is delegated to a remote service that can be OpenEdX, KeyCloack, or @@ -26,7 +26,7 @@ set for the above authentication delegation BASE_URL setting.

    If you need to bind user data into a url, wrap the property between brackets. For example, the link configured above for the profile page {base_url:s}/u/(username) would point to https://lms.example.com/u/johndoe for a user carrying the username johndoe.

    - - + + \ No newline at end of file diff --git a/docs/2.9.0/django-react-interop/index.html b/docs/2.9.0/django-react-interop/index.html index 8fa3315ce5..a576067827 100644 --- a/docs/2.9.0/django-react-interop/index.html +++ b/docs/2.9.0/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.9.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.9.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.9.0/docker-development/index.html b/docs/2.9.0/docker-development/index.html index fad80f38e1..b8aa70324f 100644 --- a/docs/2.9.0/docker-development/index.html +++ b/docs/2.9.0/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.9.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.9.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.9.0/frontend-overrides/index.html b/docs/2.9.0/frontend-overrides/index.html index 165e18ad29..d5b43d1e5f 100644 --- a/docs/2.9.0/frontend-overrides/index.html +++ b/docs/2.9.0/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.9.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.9.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.9.0/internationalization/index.html b/docs/2.9.0/internationalization/index.html index f389b508f6..712ada5776 100644 --- a/docs/2.9.0/internationalization/index.html +++ b/docs/2.9.0/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.9.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.9.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.9.0/lms-backends/index.html b/docs/2.9.0/lms-backends/index.html index 8317bbbf5b..0236a517bd 100644 --- a/docs/2.9.0/lms-backends/index.html +++ b/docs/2.9.0/lms-backends/index.html @@ -4,12 +4,12 @@ Configuring LMS Backends | Richie - - + +
    -
    Version: 2.9.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless +

    Version: 2.9.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless experience between browsing the course catalog on Richie, enrolling to a course and following the course itself on the LMS.

    It is possible to do the same with Moodle or any other LMS exposing an enrollment API, at the cost of writing a custom LMS handler backend.

    Prerequisites

    This connection requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that @@ -36,7 +36,7 @@ value other then manual.

    • Type: enum(string)
    • Required: No
    • Value: for example ["languages"]

    Technical support

    If you encounter an issue with this documentation or the backends included in Richie, please open an issue on our repository.

    If you need a custom backend, you can submit a PR or open an issue and we will consider adding it.

    - - + + \ No newline at end of file diff --git a/docs/2.9.0/lms-connection/index.html b/docs/2.9.0/lms-connection/index.html index c28a8e19bb..1e24685a67 100644 --- a/docs/2.9.0/lms-connection/index.html +++ b/docs/2.9.0/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with one or more LMS | Richie - - + +
    -
    Version: 2.9.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated +

    Version: 2.9.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated seamless experience.

    As of today, each approach has been implemented for OpenEdX but the same could be done for other LMSes like Moodle, at the cost of minor adaptations.

    1. Displaying connection status

    OpenEdX can be configured to allow CORS requests. Doing so allows Richie to retrieve a user's connection status from OpenEdx and display the user's profile information directly on the Richie @@ -31,7 +31,7 @@ docker-compose.

    If you want to activate seamless enrollment locally for development, you will need to set up TLS domains for both Richie and OpenEdX. To do this, head over to our guide on setting-up TLS connections for Richie and OpenEdX.

    - - + + \ No newline at end of file diff --git a/docs/2.9.0/native-installation/index.html b/docs/2.9.0/native-installation/index.html index adc7624c8a..0f42eff72f 100644 --- a/docs/2.9.0/native-installation/index.html +++ b/docs/2.9.0/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.9.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.9.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.9.0/synchronizing-course-runs/index.html b/docs/2.9.0/synchronizing-course-runs/index.html index db0b618327..35457ce814 100644 --- a/docs/2.9.0/synchronizing-course-runs/index.html +++ b/docs/2.9.0/synchronizing-course-runs/index.html @@ -4,12 +4,12 @@ Synchronizing course runs between Richie and OpenEdX | Richie - - + +
    -
    Version: 2.9.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the +

    Version: 2.9.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the RICHIE_COURSE_RUN_SYNC_SECRETS setting with one or more secrets:

    RICHIE_COURSE_RUN_SYNC_SECRETS = ["SharedSecret", "OtherSharedSecret"]

    This setting collects several secrets in order to allow rotating them without any downtime. Any of the secrets listed in this setting can be used to sign your queries.

    Your secret should be shared with the LMS or distant system that needs to synchronize its course runs with the Richie instance. Richie will try the declared secrets one by one until it finds @@ -29,7 +29,7 @@ course run is modified and published.

    Or you can use the Richie Open edX Synchronization which is based on the following code sample and also includes the enrollment count.

    Given a COURSE_HOOK setting defined as follows in your OpenEdX instance:

    COURSE_HOOK = {
    "secret": "SharedSecret",
    "url": "https://richie.example.com/api/v1.0/course-runs-sync/",
    }

    The code for the synchronization function in OpenEdX could look like this:

    import hashlib
    import hmac
    import json

    from django.conf import settings

    from microsite_configuration import microsite
    import requests
    from xmodule.modulestore.django import modulestore


    def update_course(course_key, *args, **kwargs):
    """Synchronize an OpenEdX course, identified by its course key, with a Richie instance."""
    course = modulestore().get_course(course_key)
    edxapp_domain = microsite.get_value("site_domain", settings.LMS_BASE)

    data = {
    "resource_link": "https://{:s}/courses/{!s}/info".format(
    edxapp_domain, course_key
    ),
    "start": course.start and course.start.isoformat(),
    "end": course.end and course.end.isoformat(),
    "enrollment_start": course.enrollment_start and course.enrollment_start.isoformat(),
    "enrollment_end": course.enrollment_end and course.enrollment_end.isoformat(),
    "languages": [course.language or settings.LANGUAGE_CODE],
    }

    signature = hmac.new(
    setting.COURSE_HOOK["secret"].encode("utf-8"),
    msg=json.dumps(data).encode("utf-8"),
    digestmod=hashlib.sha256,
    ).hexdigest()

    response = requests.post(
    setting.COURSE_HOOK["url"],
    json=data,
    headers={"Authorization": "SIG-HMAC-SHA256 {:s}".format(signature)},
    )

    Thanks to the signal emitted in OpenEdX, this function can then be triggered each time a course is modified and published:

    from django.dispatch import receiver
    from xmodule.modulestore.django import SignalHandler


    @receiver(SignalHandler.course_published, dispatch_uid='update_course_on_publish')
    def update_course_on_publish(sender, course_key, **kwargs):
    update_course(course_key)
    - - + + \ No newline at end of file diff --git a/docs/2.9.0/tls-connection/index.html b/docs/2.9.0/tls-connection/index.html index 68413628e0..6f8a56bd8d 100644 --- a/docs/2.9.0/tls-connection/index.html +++ b/docs/2.9.0/tls-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie and OpenEdX over TLS for development | Richie - - + +
    -
    Version: 2.9.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 +

    Version: 2.9.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 and uses a fake LMS backend (base.BaseLMSBackend) as you can see if you check the RICHIE_LMS_BACKENDS setting in env.d/development.

    This base backend uses session storage to fake enrollments to course runs.

    If you want to test real enrollments to an OpenEdX instance hosted on an external domain, OpenEdX will need to generate a CORS CSRF Cookie. This cookie is flagged as secure, which implies that @@ -32,7 +32,7 @@ to share cookies with the OpenEdx application to allow CORS CSRF Protected XHR requests.

    4. Start Richie and OpenEdx over SSL

    Now, the OpenEdx application should respond on https://edx.local.dev:8073, and Richie on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. The next time you want to use SSL, you can run the following command on both the Richie and OpenEdX projects:

    $ make run-ssl

    Of course, you can still run apps without ssl by using:

    $ make run
    - - + + \ No newline at end of file diff --git a/docs/2.9.0/web-analytics/index.html b/docs/2.9.0/web-analytics/index.html index 9be106cb06..0d10d0d634 100644 --- a/docs/2.9.0/web-analytics/index.html +++ b/docs/2.9.0/web-analytics/index.html @@ -4,16 +4,16 @@ Add web analytics to your site | Richie - - + +
    -
    Version: 2.9.0

    Add web analytics to your site

    The purpose of this file is to document how you can enable a Web Analytics solution. +

    Version: 2.9.0

    Add web analytics to your site

    The purpose of this file is to document how you can enable a Web Analytics solution. Currently Richie only supports by default the Google Analytics using the Google Tag Manager Javascript. But it is possible with little cost to add support for other web analytics solutions.

    Google Analytics

    Next, it is decribed how you can configure the Google Analytics on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Analytics tracking id code.

    The current Google Analytics implementation also includes custom dimensions. Those dimensions permit you to create further analyses on Google Analytics or even use them to create custom reports. Custom dimensions with a value as example:

    • Organizations codes - UNIV_LISBON | UNIV_PORTO
    • Course code - COURSE_XPTO
    • Course runs titles - Summer edition | Winter edition
    • Course runs resource links - http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/info
    • Page title - Introduction to Programming

    Location of the web analytics javascript

    Use the WEB_ANALYTICS_LOCATION settings to decide where do you want to put the Javascript code. Use head (default value), to put the Javascript on HTML header, or footer, to put the Javascript code to the bottom of the body.

    Add a new Web Analytics solution

    In this section it's described how you can add support to a different Web Analytics solution.

    • override the richie/web_analytics.html template
    • define the WEB_ANALYTICS_ID setting with your tracking identification
    • define the WEB_ANALYTICS_PROVIDER setting with a value that represents your solution, eg. my-custom-web-analytics-software
    • optionally change WEB_ANALYTICS_LOCATION setting with head (default) or footer value
    • Example of a richie/web_analytics.html file customization that prints to the browser console log the dimension keys and values:
    <script type="text/javascript">
    {% for dimension_key, dimension_value_list in WEB_ANALYTICS.DIMENSIONS.items %}
    console.log("dimension: index '{{forloop.counter}}' with key '{{ dimension_key }}' with value '{{ dimension_value_list|join:" | " }}'");
    {% endfor %}
    </script>

    Output:

    dimension: index '1' with key 'organizations_codes' with value 'COMPATIBLE-EVEN-KEELED-UTILIZATION-19 | FOCUSED-NEXT-GENERATION-FUNCTIONALITIES-22 | UNIVERSAL-MODULAR-LOCAL-AREA-NETWORK-23'
    dimension: index '2' with key 'course_code' with value '00017'
    dimension: index '3' with key 'course_runs_titles' with value 'Run 0'
    dimension: index '4' with key 'course_runs_resource_links' with value ''
    dimension: index '5' with key 'page_title' with value 'Business-focused zero-defect application'

    But you can also contribute to Richie by creating a pull request to add support for a different web analytics solution. In this last case, you have to edit directly the richie/web_analytics.html template.

    Example of an override of the richie/web_analytics.html file:

    {% block web_analytics %}
    {% if WEB_ANALYTICS_ID %}
    {% if WEB_ANALYTICS_PROVIDER == "my_custom_web_analytics_software" %}
    <script type="text/javascript" src="{% static 'myapp/js/custom_web_analytics_software.js' %}">
    <script type="text/javascript">
    // javascript code that startups the custom web analytics software
    </script>
    {% endif %}
    {% endif %}
    {% endblock web_analytics %}

    The web analytics dimensions are being added to the django context using the WEB_ANALYTICS.DIMENSIONS dictionary. Because each dimension value could have multiple values, then each dictionary value is a list. Web analytics dimensions dictionary keys:

    • organizations_codes
    • course_code
    • course_runs_titles
    • course_runs_resource_links
    • page_title

    Example, if you only need the organization codes on your custom richie/web_analytics.html file:

    <script type="text/javascript">
    console.log("organization codes: '{{ WEB_ANALYTICS.DIMENSIONS.organizations_codes |join:" | " }}");
    </script>
    - - + + \ No newline at end of file diff --git a/docs/2.9.1/accessibility-testing/index.html b/docs/2.9.1/accessibility-testing/index.html index 8a0f9e8f3b..6582f3dbbc 100644 --- a/docs/2.9.1/accessibility-testing/index.html +++ b/docs/2.9.1/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: 2.9.1

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.9.1

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems in any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two way to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/2.9.1/api/course-run-synchronization-api/index.html b/docs/2.9.1/api/course-run-synchronization-api/index.html index 0653556a4e..ee7d162312 100644 --- a/docs/2.9.1/api/course-run-synchronization-api/index.html +++ b/docs/2.9.1/api/course-run-synchronization-api/index.html @@ -4,12 +4,12 @@ Course run synchronization API | Richie - - + +
    -
    Version: 2.9.1

    Course run synchronization API

    API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

    Synchronization endpoint [/api/1.0/course-runs-sync]

    This documentation describes version "1.0" of this API endpoint.

    Synchronize a course run [POST]

    It takes a JSON object containing the course run details:

    • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - +
      Version: 2.9.1

      Course run synchronization API

      API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

      Synchronization endpoint [/api/1.0/course-runs-sync]

      This documentation describes version "1.0" of this API endpoint.

      Synchronize a course run [POST]

      It takes a JSON object containing the course run details:

      • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - url of the course syllabus on the LMS from which a unique course identifier can be extracted
      • start: 2018-02-01T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course starts
      • end: 2018-02-28T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course ends
      • enrollment_start: 2018-01-01T06:00:00Z (string, optional) - ISO 8601 date, when enrollment @@ -19,7 +19,7 @@ containing the digest of the utf-8 encoded json representation of the submitted data for the given secret key and SHA256 digest algorithm (see [synchronizing-course-runs] for an example).
    • Body

        {
      "resource_link": "https://lms.example.com/courses/course-v1:001+001+001/info",
      "start": "2021-02-01T00:00:00Z",
      "end": "2021-02-31T23:59:59Z",
      "enrollment_start": "2021-01-01T00:00:00Z",
      "enrollment_end": "2021-01-31T23:59:59Z",
      "languages": ["en", "fr"]
      }
  • Response 200 (application/json)

    • Body

        {
      "success": True
      }
  • - - + + \ No newline at end of file diff --git a/docs/2.9.1/building-the-frontend/index.html b/docs/2.9.1/building-the-frontend/index.html index 988094723e..11d8de030b 100644 --- a/docs/2.9.1/building-the-frontend/index.html +++ b/docs/2.9.1/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: 2.9.1

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.9.1

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/2.9.1/contributing-guide/index.html b/docs/2.9.1/contributing-guide/index.html index 8fa0e6cd27..b1936d182c 100644 --- a/docs/2.9.1/contributing-guide/index.html +++ b/docs/2.9.1/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: 2.9.1

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.9.1

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/2.9.1/css-guidelines/index.html b/docs/2.9.1/css-guidelines/index.html index 7ff316bfec..ab94309a98 100644 --- a/docs/2.9.1/css-guidelines/index.html +++ b/docs/2.9.1/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: 2.9.1

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.9.1

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/2.9.1/discover/index.html b/docs/2.9.1/discover/index.html index e6929e9238..00eb5b8768 100644 --- a/docs/2.9.1/discover/index.html +++ b/docs/2.9.1/discover/index.html @@ -4,12 +4,12 @@ Getting started with Richie | Richie - - + +
    -
    Version: 2.9.1

    Getting started with Richie

    If you're looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed +

    Version: 2.9.1

    Getting started with Richie

    If you're looking for a quick preview of Richie, you can take a look and have a tour of Richie on our dedicated demo site.

    Login/password are admin/admin. The database is regularly flushed.

    Architecture

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of 4 services:

    • db: the Postgresql database,
    • elasticsearch: the search engine,
    • app: the actual DjangoCMS project with all our application code,
    • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the CSS files from Sass sources.

    At "France Université Numérique", we deploy our applications on OpenShift/Kubernetes using @@ -24,7 +24,7 @@ a course on the LMS, and the LMS points back to the catalogue to browse courses.

    This approach is used for example on https://www.fun-campus.fr or https://catalogue.edulib.org.

    For a seamless user experience, it is possible to connect a Richie instance to an OpenEdX instance (or some other LMS like Moodle at the cost of minor adaptations), in several ways that we explain in the LMS connection guide.

    - - + + \ No newline at end of file diff --git a/docs/2.9.1/displaying-connection-status/index.html b/docs/2.9.1/displaying-connection-status/index.html index c0bbb67f44..4a73fb33f0 100644 --- a/docs/2.9.1/displaying-connection-status/index.html +++ b/docs/2.9.1/displaying-connection-status/index.html @@ -4,12 +4,12 @@ Displaying OpenEdX connection status in Richie | Richie - - + +
    -
    Version: 2.9.1

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status +

    Version: 2.9.1

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status and display profile information for the logged-in user in Richie.

    In Richie, the only users that are actually authenticated on the DjangoCMS instance producing the site, are editors and staff users. Your instructors and learners will not have user accounts on Richie, but authentication is delegated to a remote service that can be OpenEdX, KeyCloack, or @@ -26,7 +26,7 @@ set for the above authentication delegation BASE_URL setting.

    If you need to bind user data into a url, wrap the property between brackets. For example, the link configured above for the profile page {base_url:s}/u/(username) would point to https://lms.example.com/u/johndoe for a user carrying the username johndoe.

    - - + + \ No newline at end of file diff --git a/docs/2.9.1/django-react-interop/index.html b/docs/2.9.1/django-react-interop/index.html index fbe48b0772..7743e7a5c2 100644 --- a/docs/2.9.1/django-react-interop/index.html +++ b/docs/2.9.1/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: 2.9.1

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.9.1

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/2.9.1/docker-development/index.html b/docs/2.9.1/docker-development/index.html index d6098eb790..30e0d0c70c 100644 --- a/docs/2.9.1/docker-development/index.html +++ b/docs/2.9.1/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: 2.9.1

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.9.1

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/2.9.1/frontend-overrides/index.html b/docs/2.9.1/frontend-overrides/index.html index 1a93af24f0..f35ee8f173 100644 --- a/docs/2.9.1/frontend-overrides/index.html +++ b/docs/2.9.1/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: 2.9.1

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.9.1

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/2.9.1/internationalization/index.html b/docs/2.9.1/internationalization/index.html index 9740ee5987..5b281ac885 100644 --- a/docs/2.9.1/internationalization/index.html +++ b/docs/2.9.1/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: 2.9.1

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.9.1

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/2.9.1/lms-backends/index.html b/docs/2.9.1/lms-backends/index.html index 9c3816c583..1b783baf93 100644 --- a/docs/2.9.1/lms-backends/index.html +++ b/docs/2.9.1/lms-backends/index.html @@ -4,12 +4,12 @@ Configuring LMS Backends | Richie - - + +
    -
    Version: 2.9.1

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless +

    Version: 2.9.1

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless experience between browsing the course catalog on Richie, enrolling to a course and following the course itself on the LMS.

    It is possible to do the same with Moodle or any other LMS exposing an enrollment API, at the cost of writing a custom LMS handler backend.

    Prerequisites

    This connection requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that @@ -36,7 +36,7 @@ value other then manual.

    • Type: enum(string)
    • Required: No
    • Value: for example ["languages"]

    Technical support

    If you encounter an issue with this documentation or the backends included in Richie, please open an issue on our repository.

    If you need a custom backend, you can submit a PR or open an issue and we will consider adding it.

    - - + + \ No newline at end of file diff --git a/docs/2.9.1/lms-connection/index.html b/docs/2.9.1/lms-connection/index.html index 7952765e75..f8212bda45 100644 --- a/docs/2.9.1/lms-connection/index.html +++ b/docs/2.9.1/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with one or more LMS | Richie - - + +
    -
    Version: 2.9.1

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated +

    Version: 2.9.1

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated seamless experience.

    As of today, each approach has been implemented for OpenEdX but the same could be done for other LMSes like Moodle, at the cost of minor adaptations.

    1. Displaying connection status

    OpenEdX can be configured to allow CORS requests. Doing so allows Richie to retrieve a user's connection status from OpenEdx and display the user's profile information directly on the Richie @@ -31,7 +31,7 @@ docker-compose.

    If you want to activate seamless enrollment locally for development, you will need to set up TLS domains for both Richie and OpenEdX. To do this, head over to our guide on setting-up TLS connections for Richie and OpenEdX.

    - - + + \ No newline at end of file diff --git a/docs/2.9.1/native-installation/index.html b/docs/2.9.1/native-installation/index.html index 98546382cb..6444521637 100644 --- a/docs/2.9.1/native-installation/index.html +++ b/docs/2.9.1/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: 2.9.1

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.9.1

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/2.9.1/synchronizing-course-runs/index.html b/docs/2.9.1/synchronizing-course-runs/index.html index 28be56b787..8b961f3ca3 100644 --- a/docs/2.9.1/synchronizing-course-runs/index.html +++ b/docs/2.9.1/synchronizing-course-runs/index.html @@ -4,12 +4,12 @@ Synchronizing course runs between Richie and OpenEdX | Richie - - + +
    -
    Version: 2.9.1

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the +

    Version: 2.9.1

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the RICHIE_COURSE_RUN_SYNC_SECRETS setting with one or more secrets:

    RICHIE_COURSE_RUN_SYNC_SECRETS = ["SharedSecret", "OtherSharedSecret"]

    This setting collects several secrets in order to allow rotating them without any downtime. Any of the secrets listed in this setting can be used to sign your queries.

    Your secret should be shared with the LMS or distant system that needs to synchronize its course runs with the Richie instance. Richie will try the declared secrets one by one until it finds @@ -29,7 +29,7 @@ course run is modified and published.

    Or you can use the Richie Open edX Synchronization which is based on the following code sample and also includes the enrollment count.

    Given a COURSE_HOOK setting defined as follows in your OpenEdX instance:

    COURSE_HOOK = {
    "secret": "SharedSecret",
    "url": "https://richie.example.com/api/v1.0/course-runs-sync/",
    }

    The code for the synchronization function in OpenEdX could look like this:

    import hashlib
    import hmac
    import json

    from django.conf import settings

    from microsite_configuration import microsite
    import requests
    from xmodule.modulestore.django import modulestore


    def update_course(course_key, *args, **kwargs):
    """Synchronize an OpenEdX course, identified by its course key, with a Richie instance."""
    course = modulestore().get_course(course_key)
    edxapp_domain = microsite.get_value("site_domain", settings.LMS_BASE)

    data = {
    "resource_link": "https://{:s}/courses/{!s}/info".format(
    edxapp_domain, course_key
    ),
    "start": course.start and course.start.isoformat(),
    "end": course.end and course.end.isoformat(),
    "enrollment_start": course.enrollment_start and course.enrollment_start.isoformat(),
    "enrollment_end": course.enrollment_end and course.enrollment_end.isoformat(),
    "languages": [course.language or settings.LANGUAGE_CODE],
    }

    signature = hmac.new(
    setting.COURSE_HOOK["secret"].encode("utf-8"),
    msg=json.dumps(data).encode("utf-8"),
    digestmod=hashlib.sha256,
    ).hexdigest()

    response = requests.post(
    setting.COURSE_HOOK["url"],
    json=data,
    headers={"Authorization": "SIG-HMAC-SHA256 {:s}".format(signature)},
    )

    Thanks to the signal emitted in OpenEdX, this function can then be triggered each time a course is modified and published:

    from django.dispatch import receiver
    from xmodule.modulestore.django import SignalHandler


    @receiver(SignalHandler.course_published, dispatch_uid='update_course_on_publish')
    def update_course_on_publish(sender, course_key, **kwargs):
    update_course(course_key)
    - - + + \ No newline at end of file diff --git a/docs/2.9.1/tls-connection/index.html b/docs/2.9.1/tls-connection/index.html index fc92d09600..8ac1f92292 100644 --- a/docs/2.9.1/tls-connection/index.html +++ b/docs/2.9.1/tls-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie and OpenEdX over TLS for development | Richie - - + +
    -
    Version: 2.9.1

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 +

    Version: 2.9.1

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 and uses a fake LMS backend (base.BaseLMSBackend) as you can see if you check the RICHIE_LMS_BACKENDS setting in env.d/development.

    This base backend uses session storage to fake enrollments to course runs.

    If you want to test real enrollments to an OpenEdX instance hosted on an external domain, OpenEdX will need to generate a CORS CSRF Cookie. This cookie is flagged as secure, which implies that @@ -32,7 +32,7 @@ to share cookies with the OpenEdx application to allow CORS CSRF Protected XHR requests.

    4. Start Richie and OpenEdx over SSL

    Now, the OpenEdx application should respond on https://edx.local.dev:8073, and Richie on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. The next time you want to use SSL, you can run the following command on both the Richie and OpenEdX projects:

    $ make run-ssl

    Of course, you can still run apps without ssl by using:

    $ make run
    - - + + \ No newline at end of file diff --git a/docs/2.9.1/web-analytics/index.html b/docs/2.9.1/web-analytics/index.html index c2597e36d4..3269f63013 100644 --- a/docs/2.9.1/web-analytics/index.html +++ b/docs/2.9.1/web-analytics/index.html @@ -4,16 +4,16 @@ Add web analytics to your site | Richie - - + +
    -
    Version: 2.9.1

    Add web analytics to your site

    The purpose of this file is to document how you can enable a Web Analytics solution. +

    Version: 2.9.1

    Add web analytics to your site

    The purpose of this file is to document how you can enable a Web Analytics solution. Currently Richie only supports by default the Google Analytics using the Google Tag Manager Javascript. But it is possible with little cost to add support for other web analytics solutions.

    Google Analytics

    Next, it is decribed how you can configure the Google Analytics on your Richie site.

    • Add the WEB_ANALYTICS_ID setting, with your Google Analytics tracking id code.

    The current Google Analytics implementation also includes custom dimensions. Those dimensions permit you to create further analyses on Google Analytics or even use them to create custom reports. Custom dimensions with a value as example:

    • Organizations codes - UNIV_LISBON | UNIV_PORTO
    • Course code - COURSE_XPTO
    • Course runs titles - Summer edition | Winter edition
    • Course runs resource links - http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/info
    • Page title - Introduction to Programming

    Location of the web analytics javascript

    Use the WEB_ANALYTICS_LOCATION settings to decide where do you want to put the Javascript code. Use head (default value), to put the Javascript on HTML header, or footer, to put the Javascript code to the bottom of the body.

    Add a new Web Analytics solution

    In this section it's described how you can add support to a different Web Analytics solution.

    • override the richie/web_analytics.html template
    • define the WEB_ANALYTICS_ID setting with your tracking identification
    • define the WEB_ANALYTICS_PROVIDER setting with a value that represents your solution, eg. my-custom-web-analytics-software
    • optionally change WEB_ANALYTICS_LOCATION setting with head (default) or footer value
    • Example of a richie/web_analytics.html file customization that prints to the browser console log the dimension keys and values:
    <script type="text/javascript">
    {% for dimension_key, dimension_value_list in WEB_ANALYTICS.DIMENSIONS.items %}
    console.log("dimension: index '{{forloop.counter}}' with key '{{ dimension_key }}' with value '{{ dimension_value_list|join:" | " }}'");
    {% endfor %}
    </script>

    Output:

    dimension: index '1' with key 'organizations_codes' with value 'COMPATIBLE-EVEN-KEELED-UTILIZATION-19 | FOCUSED-NEXT-GENERATION-FUNCTIONALITIES-22 | UNIVERSAL-MODULAR-LOCAL-AREA-NETWORK-23'
    dimension: index '2' with key 'course_code' with value '00017'
    dimension: index '3' with key 'course_runs_titles' with value 'Run 0'
    dimension: index '4' with key 'course_runs_resource_links' with value ''
    dimension: index '5' with key 'page_title' with value 'Business-focused zero-defect application'

    But you can also contribute to Richie by creating a pull request to add support for a different web analytics solution. In this last case, you have to edit directly the richie/web_analytics.html template.

    Example of an override of the richie/web_analytics.html file:

    {% block web_analytics %}
    {% if WEB_ANALYTICS_ID %}
    {% if WEB_ANALYTICS_PROVIDER == "my_custom_web_analytics_software" %}
    <script type="text/javascript" src="{% static 'myapp/js/custom_web_analytics_software.js' %}">
    <script type="text/javascript">
    // javascript code that startups the custom web analytics software
    </script>
    {% endif %}
    {% endif %}
    {% endblock web_analytics %}

    The web analytics dimensions are being added to the django context using the WEB_ANALYTICS.DIMENSIONS dictionary. Because each dimension value could have multiple values, then each dictionary value is a list. Web analytics dimensions dictionary keys:

    • organizations_codes
    • course_code
    • course_runs_titles
    • course_runs_resource_links
    • page_title

    Example, if you only need the organization codes on your custom richie/web_analytics.html file:

    <script type="text/javascript">
    console.log("organization codes: '{{ WEB_ANALYTICS.DIMENSIONS.organizations_codes |join:" | " }}");
    </script>
    - - + + \ No newline at end of file diff --git a/docs/accessibility-testing/index.html b/docs/accessibility-testing/index.html index 3f2073ff7c..8b1430f69d 100644 --- a/docs/accessibility-testing/index.html +++ b/docs/accessibility-testing/index.html @@ -1,16 +1,16 @@ - + -Automated accessibility checks | Richie - - +Automated accessibility checks | Richie + +
    -
    Version: 2.21.1

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems on any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two ways to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: 2.22.0

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems on any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two ways to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/api/course-run-synchronization-api/index.html b/docs/api/course-run-synchronization-api/index.html index 8f873b8d55..c06b4a27c0 100644 --- a/docs/api/course-run-synchronization-api/index.html +++ b/docs/api/course-run-synchronization-api/index.html @@ -1,15 +1,15 @@ - + -Course run synchronization API | Richie - - +Course run synchronization API | Richie + +
    -
    Version: 2.21.1

    Course run synchronization API

    API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

    Synchronization endpoint [/api/1.0/course-runs-sync]

    This documentation describes version "1.0" of this API endpoint.

    Synchronize a course run [POST]

    It takes a JSON object containing the course run details:

    • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - +
      Version: 2.22.0

      Course run synchronization API

      API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

      Synchronization endpoint [/api/1.0/course-runs-sync]

      This documentation describes version "1.0" of this API endpoint.

      Synchronize a course run [POST]

      It takes a JSON object containing the course run details:

      • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - url of the course syllabus on the LMS from which a unique course identifier can be extracted
      • start: 2018-02-01T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course starts
      • end: 2018-02-28T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course ends
      • enrollment_start: 2018-01-01T06:00:00Z (string, optional) - ISO 8601 date, when enrollment @@ -18,8 +18,8 @@ languages
      • Request (application/json)

        • Headers

          • Authorization: SIG-HMAC-SHA256 xxxxxxx (string, required) - Authorization header containing the digest of the utf-8 encoded json representation of the submitted data for the given secret key and SHA256 digest algorithm (see [synchronizing-course-runs] -for an example).
        • Body

            {
          "resource_link": "https://lms.example.com/courses/course-v1:001+001+001/info",
          "start": "2021-02-01T00:00:00Z",
          "end": "2021-02-31T23:59:59Z",
          "enrollment_start": "2021-01-01T00:00:00Z",
          "enrollment_end": "2021-01-31T23:59:59Z",
          "languages": ["en", "fr"]
          }
      • Response 200 (application/json)

        • Body

            {
          "success": True
          }
    - - +for an example).
  • Body

      {
    "resource_link": "https://lms.example.com/courses/course-v1:001+001+001/info",
    "start": "2021-02-01T00:00:00Z",
    "end": "2021-02-31T23:59:59Z",
    "enrollment_start": "2021-01-01T00:00:00Z",
    "enrollment_end": "2021-01-31T23:59:59Z",
    "languages": ["en", "fr"]
    }
  • Response 200 (application/json)

    • Body

        {
      "success": True
      }
  • + + \ No newline at end of file diff --git a/docs/building-the-frontend/index.html b/docs/building-the-frontend/index.html index 368f0c0ddb..307116002e 100644 --- a/docs/building-the-frontend/index.html +++ b/docs/building-the-frontend/index.html @@ -1,16 +1,16 @@ - + -Building Richie's frontend in your own project | Richie - - +Building Richie's frontend in your own project | Richie + +
    -
    Version: 2.21.1

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: 2.22.0

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/contributing-guide/index.html b/docs/contributing-guide/index.html index bafdac5fb4..0a451b92e3 100644 --- a/docs/contributing-guide/index.html +++ b/docs/contributing-guide/index.html @@ -1,22 +1,22 @@ - + -Contributing guide | Richie - - +Contributing guide | Richie + +
    -
    Version: 2.21.1

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: 2.22.0

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker -documentation

    - - +documentation

    + + \ No newline at end of file diff --git a/docs/cookiecutter/index.html b/docs/cookiecutter/index.html index 1250052ca6..e7d32137f9 100644 --- a/docs/cookiecutter/index.html +++ b/docs/cookiecutter/index.html @@ -1,20 +1,20 @@ - + -Start your own site | Richie - - +Start your own site | Richie + +
    -
    Version: 2.21.1

    Start your own site

    We use Cookiecutter to help you +

    Version: 2.22.0

    Start your own site

    We use Cookiecutter to help you set up a production-ready learning portal website based on Richie in seconds.

    Run Cookiecutter

    There are 2 options to run Cookiecutter:

    While you think of it, navigate to the directory in which you want to create your site factory:

    cd /path/to/your/code/directory

    If you chose to install Cookiecutter, you can now run it against our -template as follows:

    cookiecutter gh:openfun/richie --directory cookiecutter  --checkout v2.21.1

    If you didn't want to install it on your machine, we provide a Docker image -built with our own repository that you can use as follows:

    docker run --rm -it -u $(id -u):$(id -g) -v $PWD:/app \
    fundocker/cookiecutter gh:openfun/richie --directory cookiecutter --checkout v2.21.1

    The --directory option is to indicate that our Cookiecutter template is in +template as follows:

    cookiecutter gh:openfun/richie --directory cookiecutter  --checkout v2.22.0

    If you didn't want to install it on your machine, we provide a Docker image +built with our own repository that you can use as follows:

    docker run --rm -it -u $(id -u):$(id -g) -v $PWD:/app \
    fundocker/cookiecutter gh:openfun/richie --directory cookiecutter --checkout v2.22.0

    The --directory option is to indicate that our Cookiecutter template is in a cookiecutter directory inside our git repository and not at the root.

    You will be prompted to enter an organization name, which will determine the name of your repository. For example, if you choose "foo" as organization name, your repository will be named foo-richie-site-factory. It's @@ -29,8 +29,8 @@ factory by replaying Cookiecutter. This will override your files in the project's scaffolding but, don't worry, it will respect all the sites you will have created in the sites directory:

    cookiecutter --overwrite-if-exists gh:openfun/richie --directory=cookiecutter

    Help us improve this project

    After starting your project, please submit an issue let us know how it went and -what other features we should add to make it better.

    - - +what other features we should add to make it better.

    + + \ No newline at end of file diff --git a/docs/css-guidelines/index.html b/docs/css-guidelines/index.html index 816b9b66fd..6c3c650e0f 100644 --- a/docs/css-guidelines/index.html +++ b/docs/css-guidelines/index.html @@ -1,16 +1,16 @@ - + -CSS Guidelines | Richie - - +CSS Guidelines | Richie + +
    -
    Version: 2.21.1

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: 2.22.0

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/discover/index.html b/docs/discover/index.html index 507c58f1e1..61fa4d0a99 100644 --- a/docs/discover/index.html +++ b/docs/discover/index.html @@ -1,15 +1,15 @@ - + -Discover Richie | Richie - - +Discover Richie | Richie + +
    -
    Version: 2.21.1

    Discover Richie

    Learning Management Systems (LMS) are great tools for hosting and playing interactive online +

    Version: 2.22.0

    Discover Richie

    Learning Management Systems (LMS) are great tools for hosting and playing interactive online courses and MOOCs.

    However, if you need to build a complete website with flexible content to aggregate your courses, in several languages and from different sources, you will soon need a CMS.

    At "France Université Numérique", we wanted to build an OpenSource portal with Python and Django. That's why we built Richie on top of DjangoCMS, one of @@ -20,8 +20,8 @@ Richie on our dedicated demo site.

    It is connected back-to-back with a demo of OpenEdX running on OpenEdX Docker.

    Two users are available for testing:

    • admin: admin@example.com/admin
    • student: edx@example.com/edx

    The database is regularly flushed.

    Start your own site

    The next step after discovering Richie on the demo is to start your own project. We provide a production-ready template based on Cookiecutter that -allows you to generate your project in seconds.

    Follow the guide on starting your own Richie site with Cookiecutter.

    Once you created a new site, you will be able to fully customize it:

    - - +allows you to generate your project in seconds.

    Follow the guide on starting your own Richie site with Cookiecutter.

    Once you created a new site, you will be able to fully customize it:

    + + \ No newline at end of file diff --git a/docs/displaying-connection-status/index.html b/docs/displaying-connection-status/index.html index 89452277f9..482c3e9636 100644 --- a/docs/displaying-connection-status/index.html +++ b/docs/displaying-connection-status/index.html @@ -1,15 +1,15 @@ - + -Displaying OpenEdX connection status in Richie | Richie - - +Displaying OpenEdX connection status in Richie | Richie + +
    -
    Version: 2.21.1

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status +

    Version: 2.22.0

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status and display profile information for the logged-in user in Richie.

    In Richie, the only users that are actually authenticated on the DjangoCMS instance producing the site, are editors and staff users. Your instructors and learners will not have user accounts on Richie, but authentication is delegated to a remote service that can be OpenEdX, KeyCloack, or @@ -25,8 +25,8 @@ as follows:

        {
    "dashboard": {
    "label": _("Dashboard"),
    "href": "{base_url:s}/dashboard",
    },
    "profile": {
    "label": _("Profile"),
    "href": "{base_url:s}/u/(username)",
    },
    "account": {
    "label": _("Account"),
    "href": "{base_url:s}/account/settings",
    }
    }

    The base_url variable is used as a Python format parameter and will be replaced by the value set for the above authentication delegation BASE_URL setting.

    If you need to bind user data into a url, wrap the property between brackets. For example, the link configured above for the profile page {base_url:s}/u/(username) would point to -https://lms.example.com/u/johndoe for a user carrying the username johndoe.

    - - +https://lms.example.com/u/johndoe for a user carrying the username johndoe.

    + + \ No newline at end of file diff --git a/docs/django-react-interop/index.html b/docs/django-react-interop/index.html index 54a323a3d1..f41c463a40 100644 --- a/docs/django-react-interop/index.html +++ b/docs/django-react-interop/index.html @@ -1,16 +1,16 @@ - + -Connecting React components with Django | Richie - - +Connecting React components with Django | Richie + +
    -
    Version: 2.21.1

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: 2.22.0

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/docker-development/index.html b/docs/docker-development/index.html index 07c4a4ec09..598d54e010 100644 --- a/docs/docker-development/index.html +++ b/docs/docker-development/index.html @@ -1,15 +1,15 @@ - + -Developing Richie with Docker | Richie - - +Developing Richie with Docker | Richie + +
    -
    Version: 2.21.1

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: 2.22.0

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -28,8 +28,8 @@ file

    Cleanup

    If you work on the Docker configuration and make repeated modifications, remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the -/etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - +/etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    + + \ No newline at end of file diff --git a/docs/filters-customization/index.html b/docs/filters-customization/index.html index 08dda5307f..5688d07074 100644 --- a/docs/filters-customization/index.html +++ b/docs/filters-customization/index.html @@ -1,15 +1,15 @@ - + -Customizing search filters | Richie - - +Customizing search filters | Richie + +
    -
    Version: 2.21.1

    Customizing search filters

    You may want to customize the filters on the left side bar of the search page.

    Richie makes it easy to choose which filters you want to display among the existing filters +

    Version: 2.22.0

    Customizing search filters

    You may want to customize the filters on the left side bar of the search page.

    Richie makes it easy to choose which filters you want to display among the existing filters and in which order. You can also configure the existing filters to change their title or the way they behave. Lastly, you can completely override a filter or create your own custom filter from scratch.

    Filters configuration

    Filters must first be defined in the FILTERS_CONFIGURATION setting. It is a dictionary defining @@ -47,8 +47,8 @@ to the code of the existing filters as good examples of what is possible. The code, although not trivial, was given much care and includes many comments in an attempt to help writing new custom filters. Of course, don't hesitate to ask for help by -opening an issue!

    - - +opening an issue!

    + + \ No newline at end of file diff --git a/docs/frontend-overrides/index.html b/docs/frontend-overrides/index.html index bf297faa28..d1fdd5107d 100644 --- a/docs/frontend-overrides/index.html +++ b/docs/frontend-overrides/index.html @@ -1,16 +1,16 @@ - + -Overriding frontend components | Richie - - +Overriding frontend components | Richie + +
    -
    Version: 2.21.1

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: 2.22.0

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/installation/index.html b/docs/installation/index.html index 127acf25ba..992e33048b 100644 --- a/docs/installation/index.html +++ b/docs/installation/index.html @@ -1,15 +1,15 @@ - + -Installing Richie for development | Richie - - +Installing Richie for development | Richie + +
    -
    Version: 2.21.1

    Installing Richie for development

    Richie is a container-native application but can also be installed +

    Version: 2.22.0

    Installing Richie for development

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of:

    • 3 running services:

      • database: postgresql or mysql at your preference,
      • elasticsearch: the search engine,
      • app: the actual DjangoCMS project with all our application code.
    • 2 containers for building purposes:

      • node: used for front-end related tasks, i.e. transpiling TypeScript sources, bundling them into a JS package, and building the @@ -34,8 +34,8 @@ to an OpenEdX instance (or some other LMS like Moodle at the cost of minor adaptations), in several ways that we explain in the LMS connection guide.

        This approach is used for example on https://www.fun-mooc.fr or -https://www.nau.edu.pt.

    - - +https://www.nau.edu.pt.

    + + \ No newline at end of file diff --git a/docs/internationalization/index.html b/docs/internationalization/index.html index b65b18c811..6d8dea4eda 100644 --- a/docs/internationalization/index.html +++ b/docs/internationalization/index.html @@ -1,15 +1,15 @@ - + -Internationalization | Richie - - +Internationalization | Richie + +
    -
    Version: 2.21.1

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: 2.22.0

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -21,8 +21,8 @@ up-to-date each time strings are modified or new strings are added, and this before each release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider -contributing on the existing language if your resources to contribute are limited.

    - - +contributing on the existing language if your resources to contribute are limited.

    + + \ No newline at end of file diff --git a/docs/joanie-connection/index.html b/docs/joanie-connection/index.html index 0b79db0b01..3c708641f5 100644 --- a/docs/joanie-connection/index.html +++ b/docs/joanie-connection/index.html @@ -1,15 +1,15 @@ - + -Joanie Connection | Richie - - +Joanie Connection | Richie + +
    -
    Version: 2.21.1

    Joanie Connection

    Joanie delivers an API able to manage course +

    Version: 2.22.0

    Joanie Connection

    Joanie delivers an API able to manage course enrollment/subscription, payment and certificates delivery. Richie can be configured to display course runs and micro-credentials managed through Joanie.

    In fact, Richie treats Joanie almost like a LMS backend that's why settings are similars.

    Configuring Joanie

    All settings related to Joanie have to be declared in the JOANIE_BACKEND dictionnary @@ -39,8 +39,8 @@ lifetime of the access token defined by your authentication server. For example, if your authentication server is using djangorestframework-simplejwt to generate the access token, its lifetime is 5 minutes by default.

    Technical support

    If you encounter an issue with this documentation, please -open an issue on our repository.

    - - +open an issue on our repository.

    + + \ No newline at end of file diff --git a/docs/lms-backends/index.html b/docs/lms-backends/index.html index 8c94040b59..d2ed059663 100644 --- a/docs/lms-backends/index.html +++ b/docs/lms-backends/index.html @@ -1,15 +1,15 @@ - + -Configuring LMS Backends | Richie - - +Configuring LMS Backends | Richie + +
    -
    Version: 2.21.1

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless +

    Version: 2.22.0

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless experience between browsing the course catalog on Richie, enrolling to a course and following the course itself on the LMS.

    It is possible to do the same with Moodle or any other LMS exposing an enrollment API, at the cost of writing a custom LMS handler backend.

    Prerequisites

    This connection requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that @@ -35,8 +35,8 @@ fields manually in Richie regardless of what OpenEdX sends after an initial value is set.

    Note that this setting is only effective for course runs with the sync_mode field set to a value other then manual.

    • Type: enum(string)
    • Required: No
    • Value: for example ["languages"]

    Technical support

    If you encounter an issue with this documentation or the backends included in Richie, please open an issue on our repository.

    If you need a custom backend, you can submit a PR or -open an issue and we will consider adding it.

    - - +open an issue and we will consider adding it.

    + + \ No newline at end of file diff --git a/docs/lms-connection/index.html b/docs/lms-connection/index.html index 24378476f6..2af18163d4 100644 --- a/docs/lms-connection/index.html +++ b/docs/lms-connection/index.html @@ -1,15 +1,15 @@ - + -Connecting Richie with one or more LMS | Richie - - +Connecting Richie with one or more LMS | Richie + +
    -
    Version: 2.21.1

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated +

    Version: 2.22.0

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated seamless experience.

    As of today, each approach has been implemented for OpenEdX but the same could be done for other LMSes like Moodle, at the cost of minor adaptations.

    1. Displaying connection status

    OpenEdX can be configured to allow CORS requests. Doing so allows Richie to retrieve a user's connection status from OpenEdx and display the user's profile information directly on the Richie @@ -30,8 +30,8 @@ apps should be able to communicate with each other via the network bridge defined in docker-compose.

    If you want to activate seamless enrollment locally for development, you will need to set up TLS domains for both Richie and OpenEdX. To do this, head over to our -guide on setting-up TLS connections for Richie and OpenEdX.

    - - +guide on setting-up TLS connections for Richie and OpenEdX.

    + + \ No newline at end of file diff --git a/docs/native-installation/index.html b/docs/native-installation/index.html index c7816942fe..56d13e5f42 100644 --- a/docs/native-installation/index.html +++ b/docs/native-installation/index.html @@ -1,15 +1,15 @@ - + -Installing Richie on your machine | Richie - - +Installing Richie on your machine | Richie + +
    -
    Version: 2.21.1

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: 2.22.0

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native installation instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -33,8 +33,8 @@ dependencies via:

    yarn install
    • JS build
    npm run build
    • CSS build

    This will compile all our SCSS files into one bundle and put it in the static folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at -localhost:8000

    python sandbox/manage.py runserver
    - - +localhost:8000

    python sandbox/manage.py runserver
    + + \ No newline at end of file diff --git a/docs/next/accessibility-testing/index.html b/docs/next/accessibility-testing/index.html index f5617c4b00..7690d01382 100644 --- a/docs/next/accessibility-testing/index.html +++ b/docs/next/accessibility-testing/index.html @@ -4,13 +4,13 @@ Automated accessibility checks | Richie - - + +
    -
    Version: Next

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems on any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two ways to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    - - +
    Version: Next

    Automated accessibility checks

    Richie includes automated accessibility checks built through a Cypress end-to-end testing infrastructure.

    Automated accessibility checks can only surface around 30% of possible problems on any given page. This does not mean they are not useful, but they cannot replace human audits and developer proficiency.

    We use axe to run these checks. You can find more about axe on the axe-core GitHub repository.

    Testing environment setup

    Both Cypress and axe are used through their respective NPM packages. This means everything goes through yarn commands. You need to have node and yarn installed locally to run the tests.

    cd tests_e2e
    yarn install

    This should install everything you need.

    Running the tests

    There are two ways to use the Cypress tests: through a terminal-based runner and through the Cypress UI. Both are started through yarn but they have different use cases.

    yarn cypress run

    You can start by running the tests directly from the terminal. This is the quickest way to make sure all views pass checks (or find out which ones do not). This is also the starting point for work on running Cypress in the CI.

    yarn cypress open

    This command simply opens the Cypress UI. From there, you can run all or some of the test suites with live reloading. This is a great way to understand why some tests are failing, especially when it comes to a11y violations.

    When there are a11y violations, an assertion fails and prints out a list in the Cypress UI. You can then click on a violation to print more information in the browser console.

    Documentation reference

    + + \ No newline at end of file diff --git a/docs/next/api/course-run-synchronization-api/index.html b/docs/next/api/course-run-synchronization-api/index.html index 71c9ac5247..0d12b4ec2d 100644 --- a/docs/next/api/course-run-synchronization-api/index.html +++ b/docs/next/api/course-run-synchronization-api/index.html @@ -4,12 +4,12 @@ Course run synchronization API | Richie - - + +
    -
    Version: Next

    Course run synchronization API

    API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

    Synchronization endpoint [/api/1.0/course-runs-sync]

    This documentation describes version "1.0" of this API endpoint.

    Synchronize a course run [POST]

    It takes a JSON object containing the course run details:

    • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - +
      Version: Next

      Course run synchronization API

      API endpoint allowing remote systems to synchronize their course runs with a Richie instance.

      Synchronization endpoint [/api/1.0/course-runs-sync]

      This documentation describes version "1.0" of this API endpoint.

      Synchronize a course run [POST]

      It takes a JSON object containing the course run details:

      • resource_link: https://lms.example.com/courses/course-v1:001+001+001/info (string, required) - url of the course syllabus on the LMS from which a unique course identifier can be extracted
      • start: 2018-02-01T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course starts
      • end: 2018-02-28T06:00:00Z (string, optional) - ISO 8601 date, when this session of the course ends
      • enrollment_start: 2018-01-01T06:00:00Z (string, optional) - ISO 8601 date, when enrollment @@ -19,7 +19,7 @@ containing the digest of the utf-8 encoded json representation of the submitted data for the given secret key and SHA256 digest algorithm (see [synchronizing-course-runs] for an example).
    • Body

        {
      "resource_link": "https://lms.example.com/courses/course-v1:001+001+001/info",
      "start": "2021-02-01T00:00:00Z",
      "end": "2021-02-31T23:59:59Z",
      "enrollment_start": "2021-01-01T00:00:00Z",
      "enrollment_end": "2021-01-31T23:59:59Z",
      "languages": ["en", "fr"]
      }
  • Response 200 (application/json)

    • Body

        {
      "success": True
      }
  • - - + + \ No newline at end of file diff --git a/docs/next/building-the-frontend/index.html b/docs/next/building-the-frontend/index.html index 5d6d49e1e9..254b25f3e5 100644 --- a/docs/next/building-the-frontend/index.html +++ b/docs/next/building-the-frontend/index.html @@ -4,13 +4,13 @@ Building Richie's frontend in your own project | Richie - - + +
    -
    Version: Next

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    - - +
    Version: Next

    Building Richie's frontend in your own project

    Richie offers plenty of opportunities to customize the way it works and make it suit the needs of your own project. Most of these go through Django settings.

    Part of Richie is a React frontend however. If you want to change how it works in ways that cannot be changed from the Django settings, you will need to build your own frontend.

    Installing richie-education

    If you have not already, you should create a directory for the frontend in your project. We recommend you mirror Richie's file structure so it's easier to keep track of the changes you make.

    mkdir -p src/frontend

    Then, you need to bootstrap your own frontend project in this new directory.

    cd src/frontend
    yarn init

    With each version of Richie, we build and publish an NPM package to enable Richie users to build their own Javascript and CSS. You're now ready to install it.

    yarn add richie-education

    In your package.json file, you should see it in the list of dependencies. Also, there's a node_modules directory where the package and its dependencies are actually installed.

    "dependencies": {
    "richie-education": "1.12.0"
    },

    Building the Javascript bundle

    You are now ready to run your own frontend build. We'll just be using webpack directly.

    yarn webpack --config node_modules/richie-education/webpack.config.js --output-path ./build --richie-dependent-build

    Here is everything that is happening:

    • yarn webpack — run the webpack CLI;
    • --config node_modules/richie-education/webpack.config.js — point webpack to richie-education's webpack config file;
    • --output-path ./build — make sure we get our output where we need it to be;
    • --richie-dependent-build — enable some affordances with import paths. We pre-configured Richie's webpack to be able to run it from a dependent project.

    You can now run your build to change frontend settings or override frontend components with your own.

    Building the CSS

    If you want to change styles in Richie, or add new styles for components & templates you develop yourself, you can run the SASS/CSS build yourself.

    Start by creating your own main file. The _ underscore at the beginning is there to prevent sass from auto-compiling the file.

    mkdir -p src/frontend/scss
    touch src/frontend/scss/_mains.scss

    Start by importing Richie's main scss file. If you prefer, you can also directly import any files you want to include — in effect re-doing Richie's _main.scss on your own.

    @import "richie-education/scss/main";

    You are now ready to run the CSS build:

    cd src/frontend
    yarn build-sass

    This gives you one output CSS file that you can put in the static files directory of your project and use to override Richie's style or add your own parts.

    + + \ No newline at end of file diff --git a/docs/next/contributing-guide/index.html b/docs/next/contributing-guide/index.html index 179e750cf8..bb14032484 100644 --- a/docs/next/contributing-guide/index.html +++ b/docs/next/contributing-guide/index.html @@ -4,19 +4,19 @@ Contributing guide | Richie - - + +
    -
    Version: Next

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations +

    Version: Next

    Contributing guide

    This project is intended to be community-driven, so please, do not hesitate to get in touch if you have any question related to our implementation or design decisions.

    We try to raise our code quality standards and expect contributors to follow the recommandations from our handbook.

    Checking your code

    We use strict flake8, pylint, isort and black linters to check the validity of our backend code:

    $ make lint-back

    We use strict eslint and prettier to check the validity of our frontend code:

    $ make lint-front

    Running tests

    On the backend, we use pytest to run our test suite:

    $ make test-back

    On the frontend, we use karma to run our test suite:

    $ make test-front

    Running migrations

    The first time you start the project with make bootstrap, the db container automatically creates a fresh database named richie and performs database migrations. Each time a new database migration is added to the code, you can synchronize the database schema by running:

    $ make migrate

    Handling new dependencies

    Each time you add new front-end or back-end dependencies, you will need to rebuild the application. We recommend to use:

    $ make bootstrap

    Going further

    To see all available commands, run:

    $ make

    We also provide shortcuts for docker-compose commands as sugar scripts in the bin/ directory:

    bin
    ├── exec
    ├── pylint
    ├── pytest
    └── run

    More details and tips & tricks can be found in our development with Docker documentation

    - - + + \ No newline at end of file diff --git a/docs/next/cookiecutter/index.html b/docs/next/cookiecutter/index.html index f81e03eb34..017e024689 100644 --- a/docs/next/cookiecutter/index.html +++ b/docs/next/cookiecutter/index.html @@ -4,17 +4,17 @@ Start your own site | Richie - - + +
    -
    Version: Next

    Start your own site

    We use Cookiecutter to help you +

    Version: Next

    Start your own site

    We use Cookiecutter to help you set up a production-ready learning portal website based on Richie in seconds.

    Run Cookiecutter

    There are 2 options to run Cookiecutter:

    While you think of it, navigate to the directory in which you want to create your site factory:

    cd /path/to/your/code/directory

    If you chose to install Cookiecutter, you can now run it against our -template as follows:

    cookiecutter gh:openfun/richie --directory cookiecutter  --checkout v2.21.1

    If you didn't want to install it on your machine, we provide a Docker image -built with our own repository that you can use as follows:

    docker run --rm -it -u $(id -u):$(id -g) -v $PWD:/app \
    fundocker/cookiecutter gh:openfun/richie --directory cookiecutter --checkout v2.21.1

    The --directory option is to indicate that our Cookiecutter template is in +template as follows:

    cookiecutter gh:openfun/richie --directory cookiecutter  --checkout v2.22.0

    If you didn't want to install it on your machine, we provide a Docker image +built with our own repository that you can use as follows:

    docker run --rm -it -u $(id -u):$(id -g) -v $PWD:/app \
    fundocker/cookiecutter gh:openfun/richie --directory cookiecutter --checkout v2.22.0

    The --directory option is to indicate that our Cookiecutter template is in a cookiecutter directory inside our git repository and not at the root.

    You will be prompted to enter an organization name, which will determine the name of your repository. For example, if you choose "foo" as organization name, your repository will be named foo-richie-site-factory. It's @@ -29,8 +29,8 @@ factory by replaying Cookiecutter. This will override your files in the project's scaffolding but, don't worry, it will respect all the sites you will have created in the sites directory:

    cookiecutter --overwrite-if-exists gh:openfun/richie --directory=cookiecutter

    Help us improve this project

    After starting your project, please submit an issue let us know how it went and -what other features we should add to make it better.

    - - +what other features we should add to make it better.

    + + \ No newline at end of file diff --git a/docs/next/css-guidelines/index.html b/docs/next/css-guidelines/index.html index 4789e055dd..388bb358af 100644 --- a/docs/next/css-guidelines/index.html +++ b/docs/next/css-guidelines/index.html @@ -4,13 +4,13 @@ CSS Guidelines | Richie - - + +
    -
    Version: Next

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    - - +
    Version: Next

    CSS Guidelines

    The purpose of these CSS guidelines is to make our CSS as easy as possible to maintain, prune and/or modify over time. To that end, they forgo some of the unwanted parts of CSS. They also require the use of a CSS preprocessor (we picked SASS) to be used effortlessly.

    Our approach is two-pronged. First, we put every piece of CSS as close as we can to the place it is used in a template or component. Second, we use strict class naming rules that act as a replacement to CSS selector combinations.

    File structuration

    Rules should be placed where their purpose is most apparent, and where they are easiest to find.

    Generally, this means CSS rules should live as close as possible to the place they are used. For example, the selectors and rules that define the look for a component should live in a .scss file in the same folder as the JS file for this component. This goes for templates too. Such files can only contain rules that are specific to this component/template and this one only

    Only general base rules, utility rules, site layout rules as well as our custom variables should live in the central app/static/scss folder.

    Code structuration

    In order to understand what classes are about at a glance and avoid collisions, naming conventions must be enforced for classes.

    Following the BEM naming convention, we will write our classes as follows :

    .block {}
    .block__element {}
    .block--modifier {}
    • .block represents the higher level of an abstraction or component.
    • .block__element represents a descendent of .block that helps form .block as a whole.
    • .block--modifier represents a different state or version of .block.

    We use double hyphens and double underscores as delimiters so that names themselves can be hyphen-delimited (e.g. .site-search__field--full).

    Yes, this notation is ugly. However, it allows our classes to express what they are doing. Both our CSS and our markup become more meaningful. It allows us to easily see what classes are related to others, and how they are related, when we look at the markup.

    Quick pointers

    • In general, do not use IDs. They can cause specificity wars and are not supposed to be reusable, and are therefore not very useful.
    • Do not nest selectors unnecessarily. It will increase specificity and affect where else you can use your styles.
    • Do not qualify selectors unnecessarily. It will impact the number of different elements you can apply styles to.
    • Comment profusely, whenever you think the CSS is getting complex or it would not be easy to understand its intent.
    • Use !important proactively. !important is a very useful tool when used proactively to make a state or a very specific rule on a tightly-scoped selector stronger. It is however to be avoided at all costs as an easy solution to specificity issues, reactively.

    (Direct) child selectors can sometimes be useful. Please remember that the key selector to determine performance is the rightmost one. i.e. div > #example is A LOT more efficient than #example > div although it may appear otherwise at first glance. Browsers parse multi part selectors from the right. See CSS Wizardry for more details.

    + + \ No newline at end of file diff --git a/docs/next/discover/index.html b/docs/next/discover/index.html index cc3ab0ceb9..1a8bd58a5e 100644 --- a/docs/next/discover/index.html +++ b/docs/next/discover/index.html @@ -4,12 +4,12 @@ Discover Richie | Richie - - + +
    -
    Version: Next

    Discover Richie

    Learning Management Systems (LMS) are great tools for hosting and playing interactive online +

    Version: Next

    Discover Richie

    Learning Management Systems (LMS) are great tools for hosting and playing interactive online courses and MOOCs.

    However, if you need to build a complete website with flexible content to aggregate your courses, in several languages and from different sources, you will soon need a CMS.

    At "France Université Numérique", we wanted to build an OpenSource portal with Python and Django. That's why we built Richie on top of DjangoCMS, one of @@ -21,7 +21,7 @@ OpenEdX Docker.

    Two users are available for testing:

    • admin: admin@example.com/admin
    • student: edx@example.com/edx

    The database is regularly flushed.

    Start your own site

    The next step after discovering Richie on the demo is to start your own project. We provide a production-ready template based on Cookiecutter that allows you to generate your project in seconds.

    Follow the guide on starting your own Richie site with Cookiecutter.

    Once you created a new site, you will be able to fully customize it:

    - - + + \ No newline at end of file diff --git a/docs/next/displaying-connection-status/index.html b/docs/next/displaying-connection-status/index.html index 36ae87333a..c58fe0514a 100644 --- a/docs/next/displaying-connection-status/index.html +++ b/docs/next/displaying-connection-status/index.html @@ -4,12 +4,12 @@ Displaying OpenEdX connection status in Richie | Richie - - + +
    -
    Version: Next

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status +

    Version: Next

    Displaying OpenEdX connection status in Richie

    This guide explains how to configure Richie and OpenEdX to share the OpenEdX connection status and display profile information for the logged-in user in Richie.

    In Richie, the only users that are actually authenticated on the DjangoCMS instance producing the site, are editors and staff users. Your instructors and learners will not have user accounts on Richie, but authentication is delegated to a remote service that can be OpenEdX, KeyCloack, or @@ -26,7 +26,7 @@ set for the above authentication delegation BASE_URL setting.

    If you need to bind user data into a url, wrap the property between brackets. For example, the link configured above for the profile page {base_url:s}/u/(username) would point to https://lms.example.com/u/johndoe for a user carrying the username johndoe.

    - - + + \ No newline at end of file diff --git a/docs/next/django-react-interop/index.html b/docs/next/django-react-interop/index.html index 8ca95e8593..43b93ace28 100644 --- a/docs/next/django-react-interop/index.html +++ b/docs/next/django-react-interop/index.html @@ -4,13 +4,13 @@ Connecting React components with Django | Richie - - + +
    -
    Version: Next

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    - - +
    Version: Next

    Connecting React components with Django

    richie is a hybrid app, that relies on both HTML pages generated by the backend (Django/DjangoCMS) based on templates, and React components rendered on the frontend for parts of these HTML pages.

    Rendering components

    We needed a convention that enables us to easily mark those areas of the page that React needs to take control of, and to tell React which component to load there.

    We decided to use a specific CSS class name along with its modifiers. We reserve the richie-react class and its modified children for this purpose.

    Additionally, components including internationalized data or strings need to know which locale to use. They will pick up the locale specified through the lang attribute of the <html> element, which is a requirement to have an accessible page anyway.

    They use the BCP47/RFC5646 format.

    <html lang="en-US">

    Example

    Here is how we would call a <FeaturedCourses /> component from a template, a plugin or a snippet:

    <div
    class="richie-react richie-react--featured-courses"
    ></div>

    When our JS is loaded, it will recognize this as an element it must take over, and render the FeaturedCourses component in this element.

    Passing properties to components

    Some of Richie's React components, and some of those you might want to write, require arguments or "props" to be passed to them. We could work around that by adding API routes to fetch these props, but that would add complexity and reduce performance.

    Instead, we decided to normalize a simpler way for components in Richie to accept input from the Django template that is adding them to the DOM.

    We can add a data-props attribute on the element with the richie-react class and write a JSON object as the value for this attribute. Each key-value pair in this object will be passed as a propName={propValue} to the React component.

    Example

    Here is how we would pass a categories={[ "sociology", "anthropology" ]} prop to our <FeaturedCourses /> component:

    <div
    class="richie-react richie-react--featured-courses"
    data-props='{"categories": ["sociology", "anthropology"]}'
    ></div>

    When the component is rendered, it will be passed a categories prop with the relevant categories.

    Built-in components

    Here are the React component that Richie comes with and uses out of the box.

    <RootSearchSuggestField />

    Renders a course search bar, like the one that appears in the default Search page.

    Interactions will send the user to the courseSearchPageUrl url passed in the props, including the selected filter and/or search terms.

    It also autocompletes user input with course names and allows users to go directly to the course page if they select a course name among the selected results.

    Props:

    • courseSearchPageUrl [required] — URL for the course search page users should be sent to when they select a suggestion that is not a course, or launch a search with text terms.
    • context [required] — see context.

    <Search />

    Renders the full-page course search engine interface, including the search results, and filters pane, but not the suggest field (which can be added separately with <SearchSuggestField />) nor the page title.

    NB: the Search Django template basically renders just this page. If you need this, use it instead. It is included here for completeness' sake.

    Props:

    • context [required] — see context.

    <SearchSuggestField />

    Renders the course search bar that interacts directly with <Search />.

    It automatically communicates with <Search /> through browser history APIs and a shared React provider. This one, unlike <RootSearchSuggestField />, is meant to be used in combination with <Search /> (on the same page).

    Props:

    • context [required] — see context.

    <UserLogin />

    Renders a component that uses the /users/whoami endpoint to determine if the user is logged in and show them the appropriate interface: Signup/Login buttons or their name along with a Logout button.

    Props:

    • loginUrl [required] — the URL where the user is sent when they click on "Log in";
    • logoutUrl [required] — a link that logs the user out and redirects them (can be the standard django logout URL);
    • signupUrl [required] — the URL where the user is sent when they click on "Sign up".

    Context

    All built-in components for Richie accept a context prop, that may be required or optional, depending on the component.

    It is used to pass app-wide contextual information pertaining to the current instance, deployment or theme of Richie.

    Here is the expected shape for this object:

    {
    assets: {
    // SVG sprite used throughout Richie
    icons: "/path/to/icons/sprite.svg"
    }
    }

    Note that it might be expanded in further versions of Richie.

    + + \ No newline at end of file diff --git a/docs/next/docker-development/index.html b/docs/next/docker-development/index.html index 0c19b6d219..55f1eac78d 100644 --- a/docs/next/docker-development/index.html +++ b/docs/next/docker-development/index.html @@ -4,12 +4,12 @@ Developing Richie with Docker | Richie - - + +
    -
    Version: Next

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django +

    Version: Next

    Developing Richie with Docker

    Now that you have Richie up and running, you can start working with it.

    Settings

    Settings are defined using Django Configurations for different environments:

    • Development: settings for development on developers' local environment,
    • Test: settings used to run our test suite,
    • ContinousIntegration: settings used on the continuous integration platform,
    • Feature: settings for deployment of each developers' feature branches,
    • Staging: settings for deployment to the staging environment,
    • PreProduction: settings for deployment to the pre-production environment,
    • Production: settings for deployment to the production environment.

    The Development environment is defined as the default environment.

    Front-end tools

    If you intend to work on the front-end development of the CMS, we also have sweet candies for you! 🤓

    # Start the Sass watcher
    $ make watch-sass

    # In a new terminal or session, start the TypeScript watcher
    $ make watch-ts

    Container control

    You can stop/start/restart a container:

    $ docker-compose [stop|start|restart] [app|postgresql|mysql|elasticsearch]

    or stop/start/restart all containers in one command:

    $ docker-compose [stop|start|restart]

    Debugging

    You can easily see the latest logs for a container:

    $ docker-compose logs [app|postgresql|mysql|elasticsearch]

    Or follow the stream of logs:

    $ docker-compose logs --follow [app|postgresql|mysql|elasticsearch]

    If you need to debug a running container, you can open a Linux shell with the @@ -29,7 +29,7 @@ remember to periodically clean the unused docker images and containers by running:

    $ docker image prune
    $ docker container prune

    Troubleshooting

    ElasticSearch service is always down

    If your elasticsearch container fails at booting, checkout the logs via:

    $ docker-compose logs elasticsearch

    You may see entries similar to:

    [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

    In this case, increase virtual memory as follows (UNIX systems):

    $ sudo sysctl -w vm/max_map_count=262144

    This fix will apply to your current session. To make it permanent on your system, edit the /etc/sysctl.conf file and add the following line:

    vm.max_map_count=262144
    - - + + \ No newline at end of file diff --git a/docs/next/filters-customization/index.html b/docs/next/filters-customization/index.html index f17c69a331..bbfab00fda 100644 --- a/docs/next/filters-customization/index.html +++ b/docs/next/filters-customization/index.html @@ -4,12 +4,12 @@ Customizing search filters | Richie - - + +
    -
    Version: Next

    Customizing search filters

    You may want to customize the filters on the left side bar of the search page.

    Richie makes it easy to choose which filters you want to display among the existing filters +

    Version: Next

    Customizing search filters

    You may want to customize the filters on the left side bar of the search page.

    Richie makes it easy to choose which filters you want to display among the existing filters and in which order. You can also configure the existing filters to change their title or the way they behave. Lastly, you can completely override a filter or create your own custom filter from scratch.

    Filters configuration

    Filters must first be defined in the FILTERS_CONFIGURATION setting. It is a dictionary defining @@ -48,7 +48,7 @@ trivial, was given much care and includes many comments in an attempt to help writing new custom filters. Of course, don't hesitate to ask for help by opening an issue!

    - - + + \ No newline at end of file diff --git a/docs/next/frontend-overrides/index.html b/docs/next/frontend-overrides/index.html index 2d41146bed..1493b5e192 100644 --- a/docs/next/frontend-overrides/index.html +++ b/docs/next/frontend-overrides/index.html @@ -4,13 +4,13 @@ Overriding frontend components | Richie - - + +
    -
    Version: Next

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    - - +
    Version: Next

    Overriding frontend components

    Once you are able to build the frontend in your project (see previous section), you can override some parts of the frontend with a drop-in replacement you built yourself.

    This enables you to customize Richie to your own needs in the same way you could do it with backend templates by overriding templates or blocks which do not suit your needs.

    Defining your overrides

    Create a json settings files somewhere in your project. You'll use it to declare the overrides for your custom Richie build.

    Currently, it is only possible to override components. Richie's build is only set up to handle them.

    Inside, create an object with only one key: "overrides". This is an object, whose key-value pairs is the name of a component as a key and the path to the drop-in replacement as the value.

    {
    "overrides": {
    "CourseGlimpse": "src/richie/components/CustomCourseGlimpse.tsx"
    }
    }

    Building a component override

    As overrides are supposed to be drop-in replacements, directly processed by the bundler instead of the original file, they need to expose the same API.

    For example, if our component to override was the following:

    export interface CourseGlimpseProps {
    course: Course;
    context: { someProp: string };
    }

    export const CourseGlimpse: React.FC<CourseGlimpseProps> = ({ course, context }) => {
    // Whatever happens in this component
    return <p>The glimpse</p>;
    };

    Then, your override needs to provide the same exports, explicitly a named CourseGlimpseProps interface and a named CourseGlimpse component.

    You also need to respect the assumptions made by other components that use your overridden version, if you are not overriding a root component.

    For example returning null might break a layout if the original component never returned such a value, etc. You also need to make sure to avoid conflict with the parameters accepted by the original component.

    Override translation

    When you create an application based on richie, you can encounter two cases about translations:

    1. You created or overrode a react component and created new translation keys
    2. You just want to override a translation in an existing richie component

    Create new translation keys

    Once you created your new component with its translation keys, you have to extract them with the following command:

      formatjs extract './**/*.ts*' --ignore ./node_modules --ignore './**/*.d.ts' --out-file './i18n/frontend.json --id-interpolation-pattern '[sha512:contenthash:base64:6]' --format crowdin

    This command extracts all translations defined in your typescript files then generates a frontend.json file in i18n/ directory. This file is like a pot file, this is the base to create your translations in any language you want.

    As --format option indicates, this command generates a file compatible with crowdin. If you want to customize this command to fit your needs, read the formatjs/cli documentation.

    Once translations keys are extracted and your local translations are ready, you need to compile these translations. In fact, the compilation process first aggregates all translation files found from provided paths then merges them with richie translations according their filename and finally generates an output formatted for react-intl. Below, here is an example of a compilation command:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/locales/*.json

    This command looks for all translation files in i18n/locales directory then merges files found with richie translation files. You can pass several path patterns. You can also use an --ignore argument to ignore a particular path.

    Override an existing translation key

    As explain above, the compilation process aggregates translations files then merges them according their filename. That means if you want override for example the english translation, you just have to create a en-US.json file and redefine translation keys used by Richie.

    Richie uses one file per language. Currently 4 languages supported:

    • English: filename is en-US.json
    • French: filename is fr-FR.json
    • Canadian french: filename is fr-CA.json
    • Spanish: filename is es-ES.json

    For example, richie uses the translation key components.UserLogin.logIn for the Log in button. If you want to change this label for the english translation, you just have to create a translation file en-US.json which redefines this translation key:

    {
    "components.UserLogin.logIn": {
    "description": "Overriden text for the login button.",
    "message": "Authentication"
    },
    }

    Then, for example if you put your overridden translation in i18n/overrides directory, you have to launch the compilation command below:

      node-modules/richie-education/i18n/compile-translations.js ./i18n/overrides/*.json

    In this way, "Authentication" will be displayed as label for login button instead of "Sign in".

    + + \ No newline at end of file diff --git a/docs/next/installation/index.html b/docs/next/installation/index.html index 65afec4b6a..be87f7b4b1 100644 --- a/docs/next/installation/index.html +++ b/docs/next/installation/index.html @@ -4,12 +4,12 @@ Installing Richie for development | Richie - - + +
    -
    Version: Next

    Installing Richie for development

    Richie is a container-native application but can also be installed +

    Version: Next

    Installing Richie for development

    Richie is a container-native application but can also be installed on your machine.

    For development, the project is defined using a docker-compose file and consists of:

    • 3 running services:

      • database: postgresql or mysql at your preference,
      • elasticsearch: the search engine,
      • app: the actual DjangoCMS project with all our application code.
    • 2 containers for building purposes:

    - - + + \ No newline at end of file diff --git a/docs/next/internationalization/index.html b/docs/next/internationalization/index.html index 2d73048391..f412bd7a6b 100644 --- a/docs/next/internationalization/index.html +++ b/docs/next/internationalization/index.html @@ -4,12 +4,12 @@ Internationalization | Richie - - + +
    -
    Version: Next

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. +

    Version: Next

    Internationalization

    richie has built-in localization and internationalization:

    • On the backend and CMS, i18n is built on the shoulders of Django and DjangoCMS,
    • On the frontend, we use React Intl.

    Contributing as a translator or proof-reader

    We use the Crowdin web platform to translate Richie to different languages. All translations are hosted at https://i18n.richie.education, which allows translators and proof-readers to contribute on translations in the languages they master.

    Sign-up on Crowdin

    If you don't have an account on Crowdin already, go to https://accounts.crowdin.com/register and fill out the form to create a free account.

    Join the Richie project

    Now that you have an account on Crowdin, @@ -22,7 +22,7 @@ release.

    Before asking for a new language, make sure it does not already exist. If your language already exists in another variant (e.g. Brazilian portuguese vs Portugal portuguese), you may consider contributing on the existing language if your resources to contribute are limited.

    - - + + \ No newline at end of file diff --git a/docs/next/joanie-connection/index.html b/docs/next/joanie-connection/index.html index efb1cd2126..25f7d03ac9 100644 --- a/docs/next/joanie-connection/index.html +++ b/docs/next/joanie-connection/index.html @@ -4,12 +4,12 @@ Joanie Connection | Richie - - + +
    -
    Version: Next

    Joanie Connection

    Joanie delivers an API able to manage course +

    Version: Next

    Joanie Connection

    Joanie delivers an API able to manage course enrollment/subscription, payment and certificates delivery. Richie can be configured to display course runs and micro-credentials managed through Joanie.

    In fact, Richie treats Joanie almost like a LMS backend that's why settings are similars.

    Configuring Joanie

    All settings related to Joanie have to be declared in the JOANIE_BACKEND dictionnary @@ -40,7 +40,7 @@ authentication server is using djangorestframework-simplejwt to generate the access token, its lifetime is 5 minutes by default.

    Technical support

    If you encounter an issue with this documentation, please open an issue on our repository.

    - - + + \ No newline at end of file diff --git a/docs/next/lms-backends/index.html b/docs/next/lms-backends/index.html index 2a241e0f21..d83729277f 100644 --- a/docs/next/lms-backends/index.html +++ b/docs/next/lms-backends/index.html @@ -4,12 +4,12 @@ Configuring LMS Backends | Richie - - + +
    -
    Version: Next

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless +

    Version: Next

    Configuring LMS Backends

    Richie can be connected to one or more OpenEdX Learning Management Systems (LMS) for a seamless experience between browsing the course catalog on Richie, enrolling to a course and following the course itself on the LMS.

    It is possible to do the same with Moodle or any other LMS exposing an enrollment API, at the cost of writing a custom LMS handler backend.

    Prerequisites

    This connection requires that Richie and OpenEdX be hosted on sibling domains i.e. domains that @@ -36,7 +36,7 @@ value other then manual.

    • Type: enum(string)
    • Required: No
    • Value: for example ["languages"]

    Technical support

    If you encounter an issue with this documentation or the backends included in Richie, please open an issue on our repository.

    If you need a custom backend, you can submit a PR or open an issue and we will consider adding it.

    - - + + \ No newline at end of file diff --git a/docs/next/lms-connection/index.html b/docs/next/lms-connection/index.html index e8adec022f..927f40c7c4 100644 --- a/docs/next/lms-connection/index.html +++ b/docs/next/lms-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie with one or more LMS | Richie - - + +
    -
    Version: Next

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated +

    Version: Next

    Connecting Richie with one or more LMS

    Connecting Richie to an LMS

    Richie can be connected to an LMS in several ways, ranging from SSO to a fully integrated seamless experience.

    As of today, each approach has been implemented for OpenEdX but the same could be done for other LMSes like Moodle, at the cost of minor adaptations.

    1. Displaying connection status

    OpenEdX can be configured to allow CORS requests. Doing so allows Richie to retrieve a user's connection status from OpenEdx and display the user's profile information directly on the Richie @@ -31,7 +31,7 @@ docker-compose.

    If you want to activate seamless enrollment locally for development, you will need to set up TLS domains for both Richie and OpenEdX. To do this, head over to our guide on setting-up TLS connections for Richie and OpenEdX.

    - - + + \ No newline at end of file diff --git a/docs/next/native-installation/index.html b/docs/next/native-installation/index.html index be63e47c65..da3374b25f 100644 --- a/docs/next/native-installation/index.html +++ b/docs/next/native-installation/index.html @@ -4,12 +4,12 @@ Installing Richie on your machine | Richie - - + +
    -
    Version: Next

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie +

    Version: Next

    Installing Richie on your machine

    This document aims to list all needed steps to have a working Richie installation on your laptop.

    A better approach is to use Docker as explained in our guide for container-native installation instructions.

    Installing a fresh server

    Version

    You need a Ubuntu 18.04 Bionic Beaver (the latest LTS version) fresh installation.

    If you are using another operating system or distribution, you can use @@ -34,7 +34,7 @@ folder we're serving.

    npm run sass

    Run server

    Make sure your database is up-to-date before running the application the first time and after each modification to your models:

    python sandbox/manage.py migrate

    You can create a superuser account:

    python sandbox/manage.py createsuperuser

    Run the tests

    python sandbox/manage.py test

    You should now be able to start Django and view the site at localhost:8000

    python sandbox/manage.py runserver
    - - + + \ No newline at end of file diff --git a/docs/next/synchronizing-course-runs/index.html b/docs/next/synchronizing-course-runs/index.html index c61ccdebde..043b5c7c0d 100644 --- a/docs/next/synchronizing-course-runs/index.html +++ b/docs/next/synchronizing-course-runs/index.html @@ -4,12 +4,12 @@ Synchronizing course runs between Richie and OpenEdX | Richie - - + +
    -
    Version: Next

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the +

    Version: Next

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the RICHIE_COURSE_RUN_SYNC_SECRETS setting with one or more secrets:

    RICHIE_COURSE_RUN_SYNC_SECRETS = ["SharedSecret", "OtherSharedSecret"]

    This setting collects several secrets in order to allow rotating them without any downtime. Any of the secrets listed in this setting can be used to sign your queries.

    Your secret should be shared with the LMS or distant system that needs to synchronize its course runs with the Richie instance. Richie will try the declared secrets one by one until it finds @@ -29,7 +29,7 @@ course run is modified and published.

    Or you can use the Richie Open edX Synchronization which is based on the following code sample and also includes the enrollment count.

    Given a COURSE_HOOK setting defined as follows in your OpenEdX instance:

    COURSE_HOOK = {
    "secret": "SharedSecret",
    "url": "https://richie.example.com/api/v1.0/course-runs-sync/",
    }

    The code for the synchronization function in OpenEdX could look like this:

    import hashlib
    import hmac
    import json

    from django.conf import settings

    from microsite_configuration import microsite
    import requests
    from xmodule.modulestore.django import modulestore


    def update_course(course_key, *args, **kwargs):
    """Synchronize an OpenEdX course, identified by its course key, with a Richie instance."""
    course = modulestore().get_course(course_key)
    edxapp_domain = microsite.get_value("site_domain", settings.LMS_BASE)

    data = {
    "resource_link": "https://{:s}/courses/{!s}/info".format(
    edxapp_domain, course_key
    ),
    "start": course.start and course.start.isoformat(),
    "end": course.end and course.end.isoformat(),
    "enrollment_start": course.enrollment_start and course.enrollment_start.isoformat(),
    "enrollment_end": course.enrollment_end and course.enrollment_end.isoformat(),
    "languages": [course.language or settings.LANGUAGE_CODE],
    }

    signature = hmac.new(
    setting.COURSE_HOOK["secret"].encode("utf-8"),
    msg=json.dumps(data).encode("utf-8"),
    digestmod=hashlib.sha256,
    ).hexdigest()

    response = requests.post(
    setting.COURSE_HOOK["url"],
    json=data,
    headers={"Authorization": "SIG-HMAC-SHA256 {:s}".format(signature)},
    )

    Thanks to the signal emitted in OpenEdX, this function can then be triggered each time a course is modified and published:

    from django.dispatch import receiver
    from xmodule.modulestore.django import SignalHandler


    @receiver(SignalHandler.course_published, dispatch_uid='update_course_on_publish')
    def update_course_on_publish(sender, course_key, **kwargs):
    update_course(course_key)
    - - + + \ No newline at end of file diff --git a/docs/next/tls-connection/index.html b/docs/next/tls-connection/index.html index 4410aff528..a6a405e238 100644 --- a/docs/next/tls-connection/index.html +++ b/docs/next/tls-connection/index.html @@ -4,12 +4,12 @@ Connecting Richie and OpenEdX over TLS for development | Richie - - + +
    -
    Version: Next

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 +

    Version: Next

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 and uses a fake LMS backend (base.BaseLMSBackend) as you can see if you check the RICHIE_LMS_BACKENDS setting in env.d/development.

    This base backend uses session storage to fake enrollments to course runs.

    If you want to test real enrollments to an OpenEdX instance hosted on an external domain, OpenEdX will need to generate a CORS CSRF Cookie. This cookie is flagged as secure, which implies that @@ -32,7 +32,7 @@ to share cookies with the OpenEdx application to allow CORS CSRF Protected XHR requests.

    4. Start Richie and OpenEdx over SSL

    Now, the OpenEdx application should respond on https://edx.local.dev:8073, and Richie on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. The next time you want to use SSL, you can run the following command on both the Richie and OpenEdX projects:

    $ make run-ssl

    Of course, you can still run apps without ssl by using:

    $ make run
    - - + + \ No newline at end of file diff --git a/docs/next/web-analytics/index.html b/docs/next/web-analytics/index.html index 75c85c2ee8..4fdaed37e6 100644 --- a/docs/next/web-analytics/index.html +++ b/docs/next/web-analytics/index.html @@ -4,19 +4,19 @@ Add web analytics to your site | Richie - - + +
    -
    Version: Next

    Add web analytics to your site

    Richie has native support to Google Universal Analytics and Google Tag Manager Web Analytics solutions. +

    Version: Next

    Add web analytics to your site

    Richie has native support to Google Universal Analytics and Google Tag Manager Web Analytics solutions. The purpose of this file is to explain how you can enable one of the supported Web Analytics providers and how you can extend Richie with an alternative solution.

    Google Universal Analytics

    Next, it is described how you can configure the Google Universal Analytics on your Richie site.

    Add the WEB_ANALYTICS setting, with the Google Universal Analytics configuration. From the next example replace TRACKING_ID with your tracking id code.

    {
    'google_universal_analytics': {
    'tracking_id': 'TRACKING_ID',
    }
    }

    The current Google Universal Analytics implementation also includes custom dimensions. Those dimensions permit you to create further analyses on Google Universal Analytics or even use them to create custom reports. Custom dimensions with a value as example:

    • Organizations codes - UNIV_LISBON | UNIV_PORTO
    • Course code - COURSE_XPTO
    • Course runs titles - Summer edition | Winter edition
    • Course runs resource links - http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/info
    • Page title - Introduction to Programming

    Google Tag

    It is possible to configure the Google Tag, gtag.js, on your Richie site.

    Add the WEB_ANALYTICS setting, with the Google Tag configuration like for example:

    {
    'google_tag': {
    'tracking_id': 'TRACKING_ID',
    }
    }

    And don't forget to replace the TRACKING_ID with your tracking id/code from Google Ads, Google Analytics, or other Google product compatible with the gtag.js.

    The Google Tag is initialized with custom dimensions like the Google Universal Analytics.

    Google Tag Manager

    Next, it is described how you can configure the Google Tag Manager, gtm.js, on your Richie site.

    Add the WEB_ANALYTICS setting, with the Google Tag Manager configuration, for example:

    {
    'google_tag_manager': {
    'tracking_id': 'TRACKING_ID',
    }
    }

    And don't forget to replace the TRACKING_ID with your GTM tracking id/code.

    The current Google Tag Manager implementation also defines a custom dimensions like the Google Universal Analytics.

    If you want to use the Environments feature of the Google Tag Manager, you need to include the environment key with its value on google_tag_manager dict inside the WEB_ANALYTICS setting.

    The environments feature in Google Tag Manager is ideal for organizations that want to preview their container changes in a test environment before those changes are published.

    {
    'google_tag_manager': {
    'tracking_id': 'TRACKING_ID',
    'environment': '&gtm_auth=aaaaaaaaaaaaaaaa&gtm_preview=env-99&gtm_cookies_win=x';
    }
    }

    Multiple Web Analytics at the same time

    It is possible to configure several web analytics solutions at the same time or the same solution with different tracking identifications.

    WEB_ANALYTICS setting example to have both Google Universal Analytics and Google Tag Manager:

    {
    'google_universal_analytics': {
    'tracking_id': 'UA-TRACKING_ID',
    },
    'google_tag_manager': {
    'tracking_id': 'GTM-TRACKING_ID',
    }
    }

    Location of the web analytics javascript

    Each web analytics js code can be put on the footer (default value), to put the Javascript on HTML body footer, or header, to put the Javascript code at the end of the HTML head.

    Update the WEB_ANALYTICS setting, like:

    {
    'google_universal_analytics': {
    'tracking_id': 'UA-TRACKING_ID',
    'location': 'footer,
    },
    }

    Add a new Web Analytics solution

    In this section it's described how you can add support to a different Web Analytics solution.

    • override the richie/web_analytics.html template
    • define the WEB_ANALYTICS setting with a value that represents your solution, eg. my-custom-web-analytics-software
    • define the WEB_ANALYTICS setting with your tracking identification
    • optionally change location with footer (default) or head value
    {
    'my-custom-web-analytics-software': {
    'tracking_id': 'MY_CUSTOM_TRACKING_ID',
    'location': 'footer,
    },
    }
    • Example of a richie/web_analytics.html file customization that prints to the browser console log the dimension keys and values:
    <script type="text/javascript">
    {% for dimension_key, dimension_value_list in WEB_ANALYTICS.DIMENSIONS.items %}
    console.log("dimension: index '{{forloop.counter}}' with key '{{ dimension_key }}' with value '{{ dimension_value_list|join:" | " }}'");
    {% endfor %}
    </script>

    Output:

    dimension: index '1' with key 'organizations_codes' with value 'COMPATIBLE-EVEN-KEELED-UTILIZATION-19 | FOCUSED-NEXT-GENERATION-FUNCTIONALITIES-22 | UNIVERSAL-MODULAR-LOCAL-AREA-NETWORK-23'
    dimension: index '2' with key 'course_code' with value '00017'
    dimension: index '3' with key 'course_runs_titles' with value 'Run 0'
    dimension: index '4' with key 'course_runs_resource_links' with value ''
    dimension: index '5' with key 'page_title' with value 'Business-focused zero-defect application'

    But you can also contribute to Richie by creating a pull request to add support for a different web analytics solution. In this last case, you have to edit directly the richie/web_analytics.html template.

    Example of an override of the richie/web_analytics.html file:

    {% extends "richie/web_analytics.html" %}
    {% block web_analytics_additional_providers %}
    {% if provider == "my_custom_web_analytics_software_provider" %}
    <script type="text/javascript" src="{% static 'myapp/js/custom_web_analytics_software.js' %}">
    <script type="text/javascript">
    // javascript code that startups the custom web analytics software
    </script>
    {% endif %}
    {% endblock web_analytics_additional_providers %}

    The web analytics dimensions are being added to the django context using the WEB_ANALYTICS.DIMENSIONS dictionary. Because each dimension value could have multiple values, then each dictionary value is a list. Web analytics dimensions dictionary keys:

    • organizations_codes
    • course_code
    • course_runs_titles
    • course_runs_resource_links
    • page_title

    Example, if you only need the organization codes on your custom richie/web_analytics.html file:

    <script type="text/javascript">
    console.log("organization codes: '{{ WEB_ANALYTICS.DIMENSIONS.organizations_codes |join:" | " }}");
    </script>

    The frontend code also sends events to the web analytics provider. Richie sends events when the user is enrolled on a course run. To support different providers, you need to create a similar file of src/frontend/js/utils/api/web-analytics/google_universal_analytics.ts and change the src/frontend/js/utils/api/web-analytics/index.ts file to include that newer provider.

    - - + + \ No newline at end of file diff --git a/docs/synchronizing-course-runs/index.html b/docs/synchronizing-course-runs/index.html index 2f47cba415..a50b1cc44f 100644 --- a/docs/synchronizing-course-runs/index.html +++ b/docs/synchronizing-course-runs/index.html @@ -1,15 +1,15 @@ - + -Synchronizing course runs between Richie and OpenEdX | Richie - - +Synchronizing course runs between Richie and OpenEdX | Richie + +
    -
    Version: 2.21.1

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the +

    Version: 2.22.0

    Synchronizing course runs between Richie and OpenEdX

    Richie can receive automatic course runs updates on a dedicated API endpoint.

    Configure a shared secret

    In order to activate the course run synchronization API endpoint, you first need to configure the RICHIE_COURSE_RUN_SYNC_SECRETS setting with one or more secrets:

    RICHIE_COURSE_RUN_SYNC_SECRETS = ["SharedSecret", "OtherSharedSecret"]

    This setting collects several secrets in order to allow rotating them without any downtime. Any of the secrets listed in this setting can be used to sign your queries.

    Your secret should be shared with the LMS or distant system that needs to synchronize its course runs with the Richie instance. Richie will try the declared secrets one by one until it finds @@ -28,8 +28,8 @@ should run on the post_publish signal emitted by the OpenEdX cms application each time a course run is modified and published.

    Or you can use the Richie Open edX Synchronization which is based on the following code sample and also includes the enrollment count.

    Given a COURSE_HOOK setting defined as follows in your OpenEdX instance:

    COURSE_HOOK = {
    "secret": "SharedSecret",
    "url": "https://richie.example.com/api/v1.0/course-runs-sync/",
    }

    The code for the synchronization function in OpenEdX could look like this:

    import hashlib
    import hmac
    import json

    from django.conf import settings

    from microsite_configuration import microsite
    import requests
    from xmodule.modulestore.django import modulestore


    def update_course(course_key, *args, **kwargs):
    """Synchronize an OpenEdX course, identified by its course key, with a Richie instance."""
    course = modulestore().get_course(course_key)
    edxapp_domain = microsite.get_value("site_domain", settings.LMS_BASE)

    data = {
    "resource_link": "https://{:s}/courses/{!s}/info".format(
    edxapp_domain, course_key
    ),
    "start": course.start and course.start.isoformat(),
    "end": course.end and course.end.isoformat(),
    "enrollment_start": course.enrollment_start and course.enrollment_start.isoformat(),
    "enrollment_end": course.enrollment_end and course.enrollment_end.isoformat(),
    "languages": [course.language or settings.LANGUAGE_CODE],
    }

    signature = hmac.new(
    setting.COURSE_HOOK["secret"].encode("utf-8"),
    msg=json.dumps(data).encode("utf-8"),
    digestmod=hashlib.sha256,
    ).hexdigest()

    response = requests.post(
    setting.COURSE_HOOK["url"],
    json=data,
    headers={"Authorization": "SIG-HMAC-SHA256 {:s}".format(signature)},
    )

    Thanks to the signal emitted in OpenEdX, this function can then be triggered each time a course -is modified and published:

    from django.dispatch import receiver
    from xmodule.modulestore.django import SignalHandler


    @receiver(SignalHandler.course_published, dispatch_uid='update_course_on_publish')
    def update_course_on_publish(sender, course_key, **kwargs):
    update_course(course_key)
    - - +is modified and published:

    from django.dispatch import receiver
    from xmodule.modulestore.django import SignalHandler


    @receiver(SignalHandler.course_published, dispatch_uid='update_course_on_publish')
    def update_course_on_publish(sender, course_key, **kwargs):
    update_course(course_key)
    + + \ No newline at end of file diff --git a/docs/tls-connection/index.html b/docs/tls-connection/index.html index d604df204a..a0ef71d2fa 100644 --- a/docs/tls-connection/index.html +++ b/docs/tls-connection/index.html @@ -1,15 +1,15 @@ - + -Connecting Richie and OpenEdX over TLS for development | Richie - - +Connecting Richie and OpenEdX over TLS for development | Richie + +
    -
    Version: 2.21.1

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 +

    Version: 2.22.0

    Connecting Richie and OpenEdX over TLS for development

    Purpose

    By default in the docker-compose environment for development, Richie is hosted on localhost:8070 and uses a fake LMS backend (base.BaseLMSBackend) as you can see if you check the RICHIE_LMS_BACKENDS setting in env.d/development.

    This base backend uses session storage to fake enrollments to course runs.

    If you want to test real enrollments to an OpenEdX instance hosted on an external domain, OpenEdX will need to generate a CORS CSRF Cookie. This cookie is flagged as secure, which implies that @@ -31,8 +31,8 @@ and Richie should respond on https://richie.local.dev:8070. The richie application should be able to share cookies with the OpenEdx application to allow CORS CSRF Protected XHR requests.

    4. Start Richie and OpenEdx over SSL

    Now, the OpenEdx application should respond on https://edx.local.dev:8073, and Richie on https://richie.local.dev:8070 without browser warning about the certificate validity.

    You need to follow these steps once. The next time you want to use SSL, you can run the following -command on both the Richie and OpenEdX projects:

    $ make run-ssl

    Of course, you can still run apps without ssl by using:

    $ make run
    - - +command on both the Richie and OpenEdX projects:

    $ make run-ssl

    Of course, you can still run apps without ssl by using:

    $ make run
    + + \ No newline at end of file diff --git a/docs/web-analytics/index.html b/docs/web-analytics/index.html index 76bf3c03e3..5f882a61c1 100644 --- a/docs/web-analytics/index.html +++ b/docs/web-analytics/index.html @@ -1,22 +1,22 @@ - + -Add web analytics to your site | Richie - - +Add web analytics to your site | Richie + +
    -
    Version: 2.21.1

    Add web analytics to your site

    Richie has native support to Google Universal Analytics and Google Tag Manager Web Analytics solutions. +

    Version: 2.22.0

    Add web analytics to your site

    Richie has native support to Google Universal Analytics and Google Tag Manager Web Analytics solutions. The purpose of this file is to explain how you can enable one of the supported Web Analytics providers and how you can extend Richie with an alternative solution.

    Google Universal Analytics

    Next, it is described how you can configure the Google Universal Analytics on your Richie site.

    Add the WEB_ANALYTICS setting, with the Google Universal Analytics configuration. From the next example replace TRACKING_ID with your tracking id code.

    {
    'google_universal_analytics': {
    'tracking_id': 'TRACKING_ID',
    }
    }

    The current Google Universal Analytics implementation also includes custom dimensions. Those dimensions permit you to create further analyses on Google Universal Analytics or even use them to create custom reports. Custom dimensions with a value as example:

    • Organizations codes - UNIV_LISBON | UNIV_PORTO
    • Course code - COURSE_XPTO
    • Course runs titles - Summer edition | Winter edition
    • Course runs resource links - http://example.edx:8073/courses/course-v1:edX+DemoX+Demo_Course/info
    • Page title - Introduction to Programming

    Google Tag

    It is possible to configure the Google Tag, gtag.js, on your Richie site.

    Add the WEB_ANALYTICS setting, with the Google Tag configuration like for example:

    {
    'google_tag': {
    'tracking_id': 'TRACKING_ID',
    }
    }

    And don't forget to replace the TRACKING_ID with your tracking id/code from Google Ads, Google Analytics, or other Google product compatible with the gtag.js.

    The Google Tag is initialized with custom dimensions like the Google Universal Analytics.

    Google Tag Manager

    Next, it is described how you can configure the Google Tag Manager, gtm.js, on your Richie site.

    Add the WEB_ANALYTICS setting, with the Google Tag Manager configuration, for example:

    {
    'google_tag_manager': {
    'tracking_id': 'TRACKING_ID',
    }
    }

    And don't forget to replace the TRACKING_ID with your GTM tracking id/code.

    The current Google Tag Manager implementation also defines a custom dimensions like the Google Universal Analytics.

    If you want to use the Environments feature of the Google Tag Manager, you need to include the environment key with its value on google_tag_manager dict inside the WEB_ANALYTICS setting.

    The environments feature in Google Tag Manager is ideal for organizations that want to preview their container changes in a test environment before those changes are published.

    {
    'google_tag_manager': {
    'tracking_id': 'TRACKING_ID',
    'environment': '&gtm_auth=aaaaaaaaaaaaaaaa&gtm_preview=env-99&gtm_cookies_win=x';
    }
    }

    Multiple Web Analytics at the same time

    It is possible to configure several web analytics solutions at the same time or the same solution with different tracking identifications.

    WEB_ANALYTICS setting example to have both Google Universal Analytics and Google Tag Manager:

    {
    'google_universal_analytics': {
    'tracking_id': 'UA-TRACKING_ID',
    },
    'google_tag_manager': {
    'tracking_id': 'GTM-TRACKING_ID',
    }
    }

    Location of the web analytics javascript

    Each web analytics js code can be put on the footer (default value), to put the Javascript on HTML body footer, or header, to put the Javascript code at the end of the HTML head.

    Update the WEB_ANALYTICS setting, like:

    {
    'google_universal_analytics': {
    'tracking_id': 'UA-TRACKING_ID',
    'location': 'footer,
    },
    }

    Add a new Web Analytics solution

    In this section it's described how you can add support to a different Web Analytics solution.

    • override the richie/web_analytics.html template
    • define the WEB_ANALYTICS setting with a value that represents your solution, eg. my-custom-web-analytics-software
    • define the WEB_ANALYTICS setting with your tracking identification
    • optionally change location with footer (default) or head value
    {
    'my-custom-web-analytics-software': {
    'tracking_id': 'MY_CUSTOM_TRACKING_ID',
    'location': 'footer,
    },
    }
    • Example of a richie/web_analytics.html file customization that prints to the browser console log the dimension keys and values:
    <script type="text/javascript">
    {% for dimension_key, dimension_value_list in WEB_ANALYTICS.DIMENSIONS.items %}
    console.log("dimension: index '{{forloop.counter}}' with key '{{ dimension_key }}' with value '{{ dimension_value_list|join:" | " }}'");
    {% endfor %}
    </script>

    Output:

    dimension: index '1' with key 'organizations_codes' with value 'COMPATIBLE-EVEN-KEELED-UTILIZATION-19 | FOCUSED-NEXT-GENERATION-FUNCTIONALITIES-22 | UNIVERSAL-MODULAR-LOCAL-AREA-NETWORK-23'
    dimension: index '2' with key 'course_code' with value '00017'
    dimension: index '3' with key 'course_runs_titles' with value 'Run 0'
    dimension: index '4' with key 'course_runs_resource_links' with value ''
    dimension: index '5' with key 'page_title' with value 'Business-focused zero-defect application'

    But you can also contribute to Richie by creating a pull request to add support for a different web analytics solution. In this last case, you have to edit directly the richie/web_analytics.html template.

    Example of an override of the richie/web_analytics.html file:

    {% extends "richie/web_analytics.html" %}
    {% block web_analytics_additional_providers %}
    {% if provider == "my_custom_web_analytics_software_provider" %}
    <script type="text/javascript" src="{% static 'myapp/js/custom_web_analytics_software.js' %}">
    <script type="text/javascript">
    // javascript code that startups the custom web analytics software
    </script>
    {% endif %}
    {% endblock web_analytics_additional_providers %}

    The web analytics dimensions are being added to the django context using the WEB_ANALYTICS.DIMENSIONS dictionary. Because each dimension value could have multiple values, then each dictionary value is a list. Web analytics dimensions dictionary keys:

    • organizations_codes
    • course_code
    • course_runs_titles
    • course_runs_resource_links
    • page_title

    Example, if you only need the organization codes on your custom richie/web_analytics.html file:

    <script type="text/javascript">
    console.log("organization codes: '{{ WEB_ANALYTICS.DIMENSIONS.organizations_codes |join:" | " }}");
    </script>

    The frontend code also sends events to the web analytics provider. Richie sends events when the user is enrolled on a course run. To support different providers, you need to create a similar file -of src/frontend/js/utils/api/web-analytics/google_universal_analytics.ts and change the src/frontend/js/utils/api/web-analytics/index.ts file to include that newer provider.

    - - +of src/frontend/js/utils/api/web-analytics/google_universal_analytics.ts and change the src/frontend/js/utils/api/web-analytics/index.ts file to include that newer provider.

    + + \ No newline at end of file diff --git a/help/index.html b/help/index.html index fe9054687d..d65323e3b8 100644 --- a/help/index.html +++ b/help/index.html @@ -4,13 +4,13 @@ Richie - - + +
    -

    Need help?

    If your questions are not answered in the docs and various readmes, reach out to us through Github Issues or by email at fun.dev@fun-mooc.fr.

    We also have a regular video-chat meetup with the stakeholders of Richie, (some) Thursdays in the afternoon, UTC time. Reach out to us to know when the next one takes place and come introduce yourself!

    Browse Docs

    Learn more in the documentation. Please tell us if anything is missing or out-of-date.

    Stay up to date

    Keep up with the latest updates on Richie by reading the changelog.

    Join the community

    This project is maintained by a dedicated group of people led by the team at France Université Numérique.

    - - +

    Need help?

    If your questions are not answered in the docs and various readmes, reach out to us through Github Issues or by email at fun.dev@fun-mooc.fr.

    We also have a regular video-chat meetup with the stakeholders of Richie, (some) Thursdays in the afternoon, UTC time. Reach out to us to know when the next one takes place and come introduce yourself!

    Browse Docs

    Learn more in the documentation. Please tell us if anything is missing or out-of-date.

    Stay up to date

    Keep up with the latest updates on Richie by reading the changelog.

    Join the community

    This project is maintained by a dedicated group of people led by the team at France Université Numérique.

    + + \ No newline at end of file diff --git a/index.html b/index.html index a911570210..c22a480cde 100644 --- a/index.html +++ b/index.html @@ -4,13 +4,13 @@ Richie - - + +
    -

    Richie helps educators create rich online learning portals

    A CMS for Open Education

    Richie helps educators create online learning portals.

    Build websites including online course catalogs in days with Richie's content management system.

    Get started

    Multilingual by Design

    From the ground up, Richie is localized and handles multilingual content.

    Built for Search

    Richie integrates a powerful course search engine with autosuggestion and advanced filtering options.

    Fully Customizable

    Personalize course catalogs by swapping colors and styles, or dive deep to customize the built-in search engine.

    Fully Open Source

    Built with Django CMS, Django, and React - everything is in the open. Richie is available under the MIT license.

    Users can modify and distribute documentation freely. Start contributing today.

    View code

    What Richie brings you

    An LMS-agnostic Education Portal

    Course catalogs can synchronize with one or more LMS instances running different software, such as Open edX or Moodle. Richie aggregates it all for your users.

    Author-first

    Content authors do not have to rely on software engineers to create and update all materials in Richie. Instead, authors use a rich editor interface to maintain content.

    Advanced Access Rights and Moderation

    Everything is managed through comprehensive access rights from CMS content structured objects like organizations, courses, and categories. Rights scale from individual users to enterprise-wide.

    A Multilingual Website

    Richie is available in more than one language, and you can add yours by talking to us. All the content can be added and managed in as many languages as you need. Richie is available in English, French, Spanish, and more. Want to add yours? Reach out! Richie supports content creation and management in as many languages as you need.

    An Extensible Platform

    Richie is a Django application with a NPM package. You can install it as a third-party app to build learning platforms.

    Join the Community

    Project stakeholders regularly check in through virtual meetups in English. Discussions take place in the #richie channel of the DjangoCMS Slack instance

    We encourage potential and new contributors to introduce themselves and get help.

    Who is Using Richie?

    France Université NumériqueNAUEDUlib
    View demo
    - - +

    Richie helps educators create rich online learning portals

    A CMS for Open Education

    Richie helps educators create online learning portals.

    Build websites including online course catalogs in days with Richie's content management system.

    Get started

    Multilingual by Design

    From the ground up, Richie is localized and handles multilingual content.

    Built for Search

    Richie integrates a powerful course search engine with autosuggestion and advanced filtering options.

    Fully Customizable

    Personalize course catalogs by swapping colors and styles, or dive deep to customize the built-in search engine.

    Fully Open Source

    Built with Django CMS, Django, and React - everything is in the open. Richie is available under the MIT license.

    Users can modify and distribute documentation freely. Start contributing today.

    View code

    What Richie brings you

    An LMS-agnostic Education Portal

    Course catalogs can synchronize with one or more LMS instances running different software, such as Open edX or Moodle. Richie aggregates it all for your users.

    Author-first

    Content authors do not have to rely on software engineers to create and update all materials in Richie. Instead, authors use a rich editor interface to maintain content.

    Advanced Access Rights and Moderation

    Everything is managed through comprehensive access rights from CMS content structured objects like organizations, courses, and categories. Rights scale from individual users to enterprise-wide.

    A Multilingual Website

    Richie is available in more than one language, and you can add yours by talking to us. All the content can be added and managed in as many languages as you need. Richie is available in English, French, Spanish, and more. Want to add yours? Reach out! Richie supports content creation and management in as many languages as you need.

    An Extensible Platform

    Richie is a Django application with a NPM package. You can install it as a third-party app to build learning platforms.

    Join the Community

    Project stakeholders regularly check in through virtual meetups in English. Discussions take place in the #richie channel of the DjangoCMS Slack instance

    We encourage potential and new contributors to introduce themselves and get help.

    Who is Using Richie?

    France Université NumériqueNAUEDUlib
    View demo
    + + \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml index 507c5fd156..dda0ffa778 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -1 +1 @@ -https://richie.education/helpweekly0.5https://richie.education/usersweekly0.5https://richie.education/versionsweekly0.5https://richie.education/docs/1.12/contributing-guideweekly0.5https://richie.education/docs/1.12/css-guidelinesweekly0.5https://richie.education/docs/1.12/discoverweekly0.5https://richie.education/docs/1.12/django-react-interopweekly0.5https://richie.education/docs/1.12/docker-developmentweekly0.5https://richie.education/docs/1.12/native-installationweekly0.5https://richie.education/docs/1.13/building-the-frontendweekly0.5https://richie.education/docs/1.13/contributing-guideweekly0.5https://richie.education/docs/1.13/css-guidelinesweekly0.5https://richie.education/docs/1.13/discoverweekly0.5https://richie.education/docs/1.13/django-react-interopweekly0.5https://richie.education/docs/1.13/docker-developmentweekly0.5https://richie.education/docs/1.13/native-installationweekly0.5https://richie.education/docs/1.14/building-the-frontendweekly0.5https://richie.education/docs/1.14/contributing-guideweekly0.5https://richie.education/docs/1.14/css-guidelinesweekly0.5https://richie.education/docs/1.14/discoverweekly0.5https://richie.education/docs/1.14/django-react-interopweekly0.5https://richie.education/docs/1.14/docker-developmentweekly0.5https://richie.education/docs/1.14/native-installationweekly0.5https://richie.education/docs/1.15/building-the-frontendweekly0.5https://richie.education/docs/1.15/contributing-guideweekly0.5https://richie.education/docs/1.15/css-guidelinesweekly0.5https://richie.education/docs/1.15/discoverweekly0.5https://richie.education/docs/1.15/django-react-interopweekly0.5https://richie.education/docs/1.15/docker-developmentweekly0.5https://richie.education/docs/1.15/native-installationweekly0.5https://richie.education/docs/1.16/accessibility-testingweekly0.5https://richie.education/docs/1.16/building-the-frontendweekly0.5https://richie.education/docs/1.16/contributing-guideweekly0.5https://richie.education/docs/1.16/css-guidelinesweekly0.5https://richie.education/docs/1.16/discoverweekly0.5https://richie.education/docs/1.16/django-react-interopweekly0.5https://richie.education/docs/1.16/docker-developmentweekly0.5https://richie.education/docs/1.16/native-installationweekly0.5https://richie.education/docs/1.17/accessibility-testingweekly0.5https://richie.education/docs/1.17/building-the-frontendweekly0.5https://richie.education/docs/1.17/contributing-guideweekly0.5https://richie.education/docs/1.17/css-guidelinesweekly0.5https://richie.education/docs/1.17/discoverweekly0.5https://richie.education/docs/1.17/django-react-interopweekly0.5https://richie.education/docs/1.17/docker-developmentweekly0.5https://richie.education/docs/1.17/native-installationweekly0.5https://richie.education/docs/2.0.0/accessibility-testingweekly0.5https://richie.education/docs/2.0.0/building-the-frontendweekly0.5https://richie.education/docs/2.0.0/contributing-guideweekly0.5https://richie.education/docs/2.0.0/css-guidelinesweekly0.5https://richie.education/docs/2.0.0/discoverweekly0.5https://richie.education/docs/2.0.0/django-react-interopweekly0.5https://richie.education/docs/2.0.0/docker-developmentweekly0.5https://richie.education/docs/2.0.0/frontend-overridesweekly0.5https://richie.education/docs/2.0.0/lms-connectionweekly0.5https://richie.education/docs/2.0.0/native-installationweekly0.5https://richie.education/docs/2.0.1/accessibility-testingweekly0.5https://richie.education/docs/2.0.1/building-the-frontendweekly0.5https://richie.education/docs/2.0.1/contributing-guideweekly0.5https://richie.education/docs/2.0.1/css-guidelinesweekly0.5https://richie.education/docs/2.0.1/discoverweekly0.5https://richie.education/docs/2.0.1/django-react-interopweekly0.5https://richie.education/docs/2.0.1/docker-developmentweekly0.5https://richie.education/docs/2.0.1/frontend-overridesweekly0.5https://richie.education/docs/2.0.1/lms-connectionweekly0.5https://richie.education/docs/2.0.1/native-installationweekly0.5https://richie.education/docs/2.1.0/accessibility-testingweekly0.5https://richie.education/docs/2.1.0/building-the-frontendweekly0.5https://richie.education/docs/2.1.0/contributing-guideweekly0.5https://richie.education/docs/2.1.0/css-guidelinesweekly0.5https://richie.education/docs/2.1.0/discoverweekly0.5https://richie.education/docs/2.1.0/django-react-interopweekly0.5https://richie.education/docs/2.1.0/docker-developmentweekly0.5https://richie.education/docs/2.1.0/frontend-overridesweekly0.5https://richie.education/docs/2.1.0/lms-connectionweekly0.5https://richie.education/docs/2.1.0/native-installationweekly0.5https://richie.education/docs/2.10.0/accessibility-testingweekly0.5https://richie.education/docs/2.10.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.10.0/building-the-frontendweekly0.5https://richie.education/docs/2.10.0/contributing-guideweekly0.5https://richie.education/docs/2.10.0/css-guidelinesweekly0.5https://richie.education/docs/2.10.0/discoverweekly0.5https://richie.education/docs/2.10.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.10.0/django-react-interopweekly0.5https://richie.education/docs/2.10.0/docker-developmentweekly0.5https://richie.education/docs/2.10.0/frontend-overridesweekly0.5https://richie.education/docs/2.10.0/internationalizationweekly0.5https://richie.education/docs/2.10.0/lms-backendsweekly0.5https://richie.education/docs/2.10.0/lms-connectionweekly0.5https://richie.education/docs/2.10.0/native-installationweekly0.5https://richie.education/docs/2.10.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.10.0/tls-connectionweekly0.5https://richie.education/docs/2.10.0/web-analyticsweekly0.5https://richie.education/docs/2.11.0/accessibility-testingweekly0.5https://richie.education/docs/2.11.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.11.0/building-the-frontendweekly0.5https://richie.education/docs/2.11.0/contributing-guideweekly0.5https://richie.education/docs/2.11.0/css-guidelinesweekly0.5https://richie.education/docs/2.11.0/discoverweekly0.5https://richie.education/docs/2.11.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.11.0/django-react-interopweekly0.5https://richie.education/docs/2.11.0/docker-developmentweekly0.5https://richie.education/docs/2.11.0/frontend-overridesweekly0.5https://richie.education/docs/2.11.0/internationalizationweekly0.5https://richie.education/docs/2.11.0/lms-backendsweekly0.5https://richie.education/docs/2.11.0/lms-connectionweekly0.5https://richie.education/docs/2.11.0/native-installationweekly0.5https://richie.education/docs/2.11.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.11.0/tls-connectionweekly0.5https://richie.education/docs/2.11.0/web-analyticsweekly0.5https://richie.education/docs/2.12.0/accessibility-testingweekly0.5https://richie.education/docs/2.12.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.12.0/building-the-frontendweekly0.5https://richie.education/docs/2.12.0/contributing-guideweekly0.5https://richie.education/docs/2.12.0/css-guidelinesweekly0.5https://richie.education/docs/2.12.0/discoverweekly0.5https://richie.education/docs/2.12.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.12.0/django-react-interopweekly0.5https://richie.education/docs/2.12.0/docker-developmentweekly0.5https://richie.education/docs/2.12.0/frontend-overridesweekly0.5https://richie.education/docs/2.12.0/internationalizationweekly0.5https://richie.education/docs/2.12.0/lms-backendsweekly0.5https://richie.education/docs/2.12.0/lms-connectionweekly0.5https://richie.education/docs/2.12.0/native-installationweekly0.5https://richie.education/docs/2.12.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.12.0/tls-connectionweekly0.5https://richie.education/docs/2.12.0/web-analyticsweekly0.5https://richie.education/docs/2.13.0/accessibility-testingweekly0.5https://richie.education/docs/2.13.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.13.0/building-the-frontendweekly0.5https://richie.education/docs/2.13.0/contributing-guideweekly0.5https://richie.education/docs/2.13.0/css-guidelinesweekly0.5https://richie.education/docs/2.13.0/discoverweekly0.5https://richie.education/docs/2.13.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.13.0/django-react-interopweekly0.5https://richie.education/docs/2.13.0/docker-developmentweekly0.5https://richie.education/docs/2.13.0/frontend-overridesweekly0.5https://richie.education/docs/2.13.0/internationalizationweekly0.5https://richie.education/docs/2.13.0/lms-backendsweekly0.5https://richie.education/docs/2.13.0/lms-connectionweekly0.5https://richie.education/docs/2.13.0/native-installationweekly0.5https://richie.education/docs/2.13.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.13.0/tls-connectionweekly0.5https://richie.education/docs/2.13.0/web-analyticsweekly0.5https://richie.education/docs/2.14.0/accessibility-testingweekly0.5https://richie.education/docs/2.14.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.14.0/building-the-frontendweekly0.5https://richie.education/docs/2.14.0/contributing-guideweekly0.5https://richie.education/docs/2.14.0/css-guidelinesweekly0.5https://richie.education/docs/2.14.0/discoverweekly0.5https://richie.education/docs/2.14.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.14.0/django-react-interopweekly0.5https://richie.education/docs/2.14.0/docker-developmentweekly0.5https://richie.education/docs/2.14.0/frontend-overridesweekly0.5https://richie.education/docs/2.14.0/internationalizationweekly0.5https://richie.education/docs/2.14.0/lms-backendsweekly0.5https://richie.education/docs/2.14.0/lms-connectionweekly0.5https://richie.education/docs/2.14.0/native-installationweekly0.5https://richie.education/docs/2.14.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.14.0/tls-connectionweekly0.5https://richie.education/docs/2.14.0/web-analyticsweekly0.5https://richie.education/docs/2.14.1/accessibility-testingweekly0.5https://richie.education/docs/2.14.1/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.14.1/building-the-frontendweekly0.5https://richie.education/docs/2.14.1/contributing-guideweekly0.5https://richie.education/docs/2.14.1/cookiecutterweekly0.5https://richie.education/docs/2.14.1/css-guidelinesweekly0.5https://richie.education/docs/2.14.1/discoverweekly0.5https://richie.education/docs/2.14.1/displaying-connection-statusweekly0.5https://richie.education/docs/2.14.1/django-react-interopweekly0.5https://richie.education/docs/2.14.1/docker-developmentweekly0.5https://richie.education/docs/2.14.1/frontend-overridesweekly0.5https://richie.education/docs/2.14.1/installationweekly0.5https://richie.education/docs/2.14.1/internationalizationweekly0.5https://richie.education/docs/2.14.1/joanie-connectionweekly0.5https://richie.education/docs/2.14.1/lms-backendsweekly0.5https://richie.education/docs/2.14.1/lms-connectionweekly0.5https://richie.education/docs/2.14.1/native-installationweekly0.5https://richie.education/docs/2.14.1/synchronizing-course-runsweekly0.5https://richie.education/docs/2.14.1/tls-connectionweekly0.5https://richie.education/docs/2.14.1/web-analyticsweekly0.5https://richie.education/docs/2.15.0/accessibility-testingweekly0.5https://richie.education/docs/2.15.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.15.0/building-the-frontendweekly0.5https://richie.education/docs/2.15.0/contributing-guideweekly0.5https://richie.education/docs/2.15.0/cookiecutterweekly0.5https://richie.education/docs/2.15.0/css-guidelinesweekly0.5https://richie.education/docs/2.15.0/discoverweekly0.5https://richie.education/docs/2.15.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.15.0/django-react-interopweekly0.5https://richie.education/docs/2.15.0/docker-developmentweekly0.5https://richie.education/docs/2.15.0/frontend-overridesweekly0.5https://richie.education/docs/2.15.0/installationweekly0.5https://richie.education/docs/2.15.0/internationalizationweekly0.5https://richie.education/docs/2.15.0/joanie-connectionweekly0.5https://richie.education/docs/2.15.0/lms-backendsweekly0.5https://richie.education/docs/2.15.0/lms-connectionweekly0.5https://richie.education/docs/2.15.0/native-installationweekly0.5https://richie.education/docs/2.15.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.15.0/tls-connectionweekly0.5https://richie.education/docs/2.15.0/web-analyticsweekly0.5https://richie.education/docs/2.15.1/accessibility-testingweekly0.5https://richie.education/docs/2.15.1/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.15.1/building-the-frontendweekly0.5https://richie.education/docs/2.15.1/contributing-guideweekly0.5https://richie.education/docs/2.15.1/cookiecutterweekly0.5https://richie.education/docs/2.15.1/css-guidelinesweekly0.5https://richie.education/docs/2.15.1/discoverweekly0.5https://richie.education/docs/2.15.1/displaying-connection-statusweekly0.5https://richie.education/docs/2.15.1/django-react-interopweekly0.5https://richie.education/docs/2.15.1/docker-developmentweekly0.5https://richie.education/docs/2.15.1/frontend-overridesweekly0.5https://richie.education/docs/2.15.1/installationweekly0.5https://richie.education/docs/2.15.1/internationalizationweekly0.5https://richie.education/docs/2.15.1/joanie-connectionweekly0.5https://richie.education/docs/2.15.1/lms-backendsweekly0.5https://richie.education/docs/2.15.1/lms-connectionweekly0.5https://richie.education/docs/2.15.1/native-installationweekly0.5https://richie.education/docs/2.15.1/synchronizing-course-runsweekly0.5https://richie.education/docs/2.15.1/tls-connectionweekly0.5https://richie.education/docs/2.15.1/web-analyticsweekly0.5https://richie.education/docs/2.16.0/accessibility-testingweekly0.5https://richie.education/docs/2.16.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.16.0/building-the-frontendweekly0.5https://richie.education/docs/2.16.0/contributing-guideweekly0.5https://richie.education/docs/2.16.0/cookiecutterweekly0.5https://richie.education/docs/2.16.0/css-guidelinesweekly0.5https://richie.education/docs/2.16.0/discoverweekly0.5https://richie.education/docs/2.16.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.16.0/django-react-interopweekly0.5https://richie.education/docs/2.16.0/docker-developmentweekly0.5https://richie.education/docs/2.16.0/frontend-overridesweekly0.5https://richie.education/docs/2.16.0/installationweekly0.5https://richie.education/docs/2.16.0/internationalizationweekly0.5https://richie.education/docs/2.16.0/joanie-connectionweekly0.5https://richie.education/docs/2.16.0/lms-backendsweekly0.5https://richie.education/docs/2.16.0/lms-connectionweekly0.5https://richie.education/docs/2.16.0/native-installationweekly0.5https://richie.education/docs/2.16.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.16.0/tls-connectionweekly0.5https://richie.education/docs/2.16.0/web-analyticsweekly0.5https://richie.education/docs/2.17.0/accessibility-testingweekly0.5https://richie.education/docs/2.17.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.17.0/building-the-frontendweekly0.5https://richie.education/docs/2.17.0/contributing-guideweekly0.5https://richie.education/docs/2.17.0/cookiecutterweekly0.5https://richie.education/docs/2.17.0/css-guidelinesweekly0.5https://richie.education/docs/2.17.0/discoverweekly0.5https://richie.education/docs/2.17.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.17.0/django-react-interopweekly0.5https://richie.education/docs/2.17.0/docker-developmentweekly0.5https://richie.education/docs/2.17.0/filters-customizationweekly0.5https://richie.education/docs/2.17.0/frontend-overridesweekly0.5https://richie.education/docs/2.17.0/installationweekly0.5https://richie.education/docs/2.17.0/internationalizationweekly0.5https://richie.education/docs/2.17.0/joanie-connectionweekly0.5https://richie.education/docs/2.17.0/lms-backendsweekly0.5https://richie.education/docs/2.17.0/lms-connectionweekly0.5https://richie.education/docs/2.17.0/native-installationweekly0.5https://richie.education/docs/2.17.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.17.0/tls-connectionweekly0.5https://richie.education/docs/2.17.0/web-analyticsweekly0.5https://richie.education/docs/2.18.0/accessibility-testingweekly0.5https://richie.education/docs/2.18.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.18.0/building-the-frontendweekly0.5https://richie.education/docs/2.18.0/contributing-guideweekly0.5https://richie.education/docs/2.18.0/cookiecutterweekly0.5https://richie.education/docs/2.18.0/css-guidelinesweekly0.5https://richie.education/docs/2.18.0/discoverweekly0.5https://richie.education/docs/2.18.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.18.0/django-react-interopweekly0.5https://richie.education/docs/2.18.0/docker-developmentweekly0.5https://richie.education/docs/2.18.0/filters-customizationweekly0.5https://richie.education/docs/2.18.0/frontend-overridesweekly0.5https://richie.education/docs/2.18.0/installationweekly0.5https://richie.education/docs/2.18.0/internationalizationweekly0.5https://richie.education/docs/2.18.0/joanie-connectionweekly0.5https://richie.education/docs/2.18.0/lms-backendsweekly0.5https://richie.education/docs/2.18.0/lms-connectionweekly0.5https://richie.education/docs/2.18.0/native-installationweekly0.5https://richie.education/docs/2.18.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.18.0/tls-connectionweekly0.5https://richie.education/docs/2.18.0/web-analyticsweekly0.5https://richie.education/docs/2.19.0/accessibility-testingweekly0.5https://richie.education/docs/2.19.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.19.0/building-the-frontendweekly0.5https://richie.education/docs/2.19.0/contributing-guideweekly0.5https://richie.education/docs/2.19.0/cookiecutterweekly0.5https://richie.education/docs/2.19.0/css-guidelinesweekly0.5https://richie.education/docs/2.19.0/discoverweekly0.5https://richie.education/docs/2.19.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.19.0/django-react-interopweekly0.5https://richie.education/docs/2.19.0/docker-developmentweekly0.5https://richie.education/docs/2.19.0/filters-customizationweekly0.5https://richie.education/docs/2.19.0/frontend-overridesweekly0.5https://richie.education/docs/2.19.0/installationweekly0.5https://richie.education/docs/2.19.0/internationalizationweekly0.5https://richie.education/docs/2.19.0/joanie-connectionweekly0.5https://richie.education/docs/2.19.0/lms-backendsweekly0.5https://richie.education/docs/2.19.0/lms-connectionweekly0.5https://richie.education/docs/2.19.0/native-installationweekly0.5https://richie.education/docs/2.19.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.19.0/tls-connectionweekly0.5https://richie.education/docs/2.19.0/web-analyticsweekly0.5https://richie.education/docs/2.2.0/accessibility-testingweekly0.5https://richie.education/docs/2.2.0/building-the-frontendweekly0.5https://richie.education/docs/2.2.0/contributing-guideweekly0.5https://richie.education/docs/2.2.0/css-guidelinesweekly0.5https://richie.education/docs/2.2.0/discoverweekly0.5https://richie.education/docs/2.2.0/django-react-interopweekly0.5https://richie.education/docs/2.2.0/docker-developmentweekly0.5https://richie.education/docs/2.2.0/frontend-overridesweekly0.5https://richie.education/docs/2.2.0/internationalizationweekly0.5https://richie.education/docs/2.2.0/lms-connectionweekly0.5https://richie.education/docs/2.2.0/native-installationweekly0.5https://richie.education/docs/2.20.0/accessibility-testingweekly0.5https://richie.education/docs/2.20.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.20.0/building-the-frontendweekly0.5https://richie.education/docs/2.20.0/contributing-guideweekly0.5https://richie.education/docs/2.20.0/cookiecutterweekly0.5https://richie.education/docs/2.20.0/css-guidelinesweekly0.5https://richie.education/docs/2.20.0/discoverweekly0.5https://richie.education/docs/2.20.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.20.0/django-react-interopweekly0.5https://richie.education/docs/2.20.0/docker-developmentweekly0.5https://richie.education/docs/2.20.0/filters-customizationweekly0.5https://richie.education/docs/2.20.0/frontend-overridesweekly0.5https://richie.education/docs/2.20.0/installationweekly0.5https://richie.education/docs/2.20.0/internationalizationweekly0.5https://richie.education/docs/2.20.0/joanie-connectionweekly0.5https://richie.education/docs/2.20.0/lms-backendsweekly0.5https://richie.education/docs/2.20.0/lms-connectionweekly0.5https://richie.education/docs/2.20.0/native-installationweekly0.5https://richie.education/docs/2.20.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.20.0/tls-connectionweekly0.5https://richie.education/docs/2.20.0/web-analyticsweekly0.5https://richie.education/docs/2.20.1/accessibility-testingweekly0.5https://richie.education/docs/2.20.1/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.20.1/building-the-frontendweekly0.5https://richie.education/docs/2.20.1/contributing-guideweekly0.5https://richie.education/docs/2.20.1/cookiecutterweekly0.5https://richie.education/docs/2.20.1/css-guidelinesweekly0.5https://richie.education/docs/2.20.1/discoverweekly0.5https://richie.education/docs/2.20.1/displaying-connection-statusweekly0.5https://richie.education/docs/2.20.1/django-react-interopweekly0.5https://richie.education/docs/2.20.1/docker-developmentweekly0.5https://richie.education/docs/2.20.1/filters-customizationweekly0.5https://richie.education/docs/2.20.1/frontend-overridesweekly0.5https://richie.education/docs/2.20.1/installationweekly0.5https://richie.education/docs/2.20.1/internationalizationweekly0.5https://richie.education/docs/2.20.1/joanie-connectionweekly0.5https://richie.education/docs/2.20.1/lms-backendsweekly0.5https://richie.education/docs/2.20.1/lms-connectionweekly0.5https://richie.education/docs/2.20.1/native-installationweekly0.5https://richie.education/docs/2.20.1/synchronizing-course-runsweekly0.5https://richie.education/docs/2.20.1/tls-connectionweekly0.5https://richie.education/docs/2.20.1/web-analyticsweekly0.5https://richie.education/docs/2.21.0/accessibility-testingweekly0.5https://richie.education/docs/2.21.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.21.0/building-the-frontendweekly0.5https://richie.education/docs/2.21.0/contributing-guideweekly0.5https://richie.education/docs/2.21.0/cookiecutterweekly0.5https://richie.education/docs/2.21.0/css-guidelinesweekly0.5https://richie.education/docs/2.21.0/discoverweekly0.5https://richie.education/docs/2.21.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.21.0/django-react-interopweekly0.5https://richie.education/docs/2.21.0/docker-developmentweekly0.5https://richie.education/docs/2.21.0/filters-customizationweekly0.5https://richie.education/docs/2.21.0/frontend-overridesweekly0.5https://richie.education/docs/2.21.0/installationweekly0.5https://richie.education/docs/2.21.0/internationalizationweekly0.5https://richie.education/docs/2.21.0/joanie-connectionweekly0.5https://richie.education/docs/2.21.0/lms-backendsweekly0.5https://richie.education/docs/2.21.0/lms-connectionweekly0.5https://richie.education/docs/2.21.0/native-installationweekly0.5https://richie.education/docs/2.21.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.21.0/tls-connectionweekly0.5https://richie.education/docs/2.21.0/web-analyticsweekly0.5https://richie.education/docs/2.3.0/accessibility-testingweekly0.5https://richie.education/docs/2.3.0/building-the-frontendweekly0.5https://richie.education/docs/2.3.0/contributing-guideweekly0.5https://richie.education/docs/2.3.0/css-guidelinesweekly0.5https://richie.education/docs/2.3.0/discoverweekly0.5https://richie.education/docs/2.3.0/django-react-interopweekly0.5https://richie.education/docs/2.3.0/docker-developmentweekly0.5https://richie.education/docs/2.3.0/frontend-overridesweekly0.5https://richie.education/docs/2.3.0/internationalizationweekly0.5https://richie.education/docs/2.3.0/lms-connectionweekly0.5https://richie.education/docs/2.3.0/native-installationweekly0.5https://richie.education/docs/2.3.1/accessibility-testingweekly0.5https://richie.education/docs/2.3.1/building-the-frontendweekly0.5https://richie.education/docs/2.3.1/contributing-guideweekly0.5https://richie.education/docs/2.3.1/css-guidelinesweekly0.5https://richie.education/docs/2.3.1/discoverweekly0.5https://richie.education/docs/2.3.1/django-react-interopweekly0.5https://richie.education/docs/2.3.1/docker-developmentweekly0.5https://richie.education/docs/2.3.1/frontend-overridesweekly0.5https://richie.education/docs/2.3.1/internationalizationweekly0.5https://richie.education/docs/2.3.1/lms-connectionweekly0.5https://richie.education/docs/2.3.1/native-installationweekly0.5https://richie.education/docs/2.3.2/accessibility-testingweekly0.5https://richie.education/docs/2.3.2/building-the-frontendweekly0.5https://richie.education/docs/2.3.2/contributing-guideweekly0.5https://richie.education/docs/2.3.2/css-guidelinesweekly0.5https://richie.education/docs/2.3.2/discoverweekly0.5https://richie.education/docs/2.3.2/django-react-interopweekly0.5https://richie.education/docs/2.3.2/docker-developmentweekly0.5https://richie.education/docs/2.3.2/frontend-overridesweekly0.5https://richie.education/docs/2.3.2/internationalizationweekly0.5https://richie.education/docs/2.3.2/lms-connectionweekly0.5https://richie.education/docs/2.3.2/native-installationweekly0.5https://richie.education/docs/2.3.3/accessibility-testingweekly0.5https://richie.education/docs/2.3.3/building-the-frontendweekly0.5https://richie.education/docs/2.3.3/contributing-guideweekly0.5https://richie.education/docs/2.3.3/css-guidelinesweekly0.5https://richie.education/docs/2.3.3/discoverweekly0.5https://richie.education/docs/2.3.3/django-react-interopweekly0.5https://richie.education/docs/2.3.3/docker-developmentweekly0.5https://richie.education/docs/2.3.3/frontend-overridesweekly0.5https://richie.education/docs/2.3.3/internationalizationweekly0.5https://richie.education/docs/2.3.3/lms-connectionweekly0.5https://richie.education/docs/2.3.3/native-installationweekly0.5https://richie.education/docs/2.4.0/accessibility-testingweekly0.5https://richie.education/docs/2.4.0/building-the-frontendweekly0.5https://richie.education/docs/2.4.0/contributing-guideweekly0.5https://richie.education/docs/2.4.0/css-guidelinesweekly0.5https://richie.education/docs/2.4.0/discoverweekly0.5https://richie.education/docs/2.4.0/django-react-interopweekly0.5https://richie.education/docs/2.4.0/docker-developmentweekly0.5https://richie.education/docs/2.4.0/frontend-overridesweekly0.5https://richie.education/docs/2.4.0/internationalizationweekly0.5https://richie.education/docs/2.4.0/lms-connectionweekly0.5https://richie.education/docs/2.4.0/native-installationweekly0.5https://richie.education/docs/2.5.0/accessibility-testingweekly0.5https://richie.education/docs/2.5.0/building-the-frontendweekly0.5https://richie.education/docs/2.5.0/contributing-guideweekly0.5https://richie.education/docs/2.5.0/css-guidelinesweekly0.5https://richie.education/docs/2.5.0/discoverweekly0.5https://richie.education/docs/2.5.0/django-react-interopweekly0.5https://richie.education/docs/2.5.0/docker-developmentweekly0.5https://richie.education/docs/2.5.0/frontend-overridesweekly0.5https://richie.education/docs/2.5.0/internationalizationweekly0.5https://richie.education/docs/2.5.0/lms-connectionweekly0.5https://richie.education/docs/2.5.0/native-installationweekly0.5https://richie.education/docs/2.6.0/accessibility-testingweekly0.5https://richie.education/docs/2.6.0/building-the-frontendweekly0.5https://richie.education/docs/2.6.0/contributing-guideweekly0.5https://richie.education/docs/2.6.0/css-guidelinesweekly0.5https://richie.education/docs/2.6.0/discoverweekly0.5https://richie.education/docs/2.6.0/django-react-interopweekly0.5https://richie.education/docs/2.6.0/docker-developmentweekly0.5https://richie.education/docs/2.6.0/frontend-overridesweekly0.5https://richie.education/docs/2.6.0/internationalizationweekly0.5https://richie.education/docs/2.6.0/lms-connectionweekly0.5https://richie.education/docs/2.6.0/native-installationweekly0.5https://richie.education/docs/2.7.0/accessibility-testingweekly0.5https://richie.education/docs/2.7.0/building-the-frontendweekly0.5https://richie.education/docs/2.7.0/contributing-guideweekly0.5https://richie.education/docs/2.7.0/css-guidelinesweekly0.5https://richie.education/docs/2.7.0/discoverweekly0.5https://richie.education/docs/2.7.0/django-react-interopweekly0.5https://richie.education/docs/2.7.0/docker-developmentweekly0.5https://richie.education/docs/2.7.0/frontend-overridesweekly0.5https://richie.education/docs/2.7.0/internationalizationweekly0.5https://richie.education/docs/2.7.0/lms-connectionweekly0.5https://richie.education/docs/2.7.0/native-installationweekly0.5https://richie.education/docs/2.7.1/accessibility-testingweekly0.5https://richie.education/docs/2.7.1/building-the-frontendweekly0.5https://richie.education/docs/2.7.1/contributing-guideweekly0.5https://richie.education/docs/2.7.1/css-guidelinesweekly0.5https://richie.education/docs/2.7.1/discoverweekly0.5https://richie.education/docs/2.7.1/django-react-interopweekly0.5https://richie.education/docs/2.7.1/docker-developmentweekly0.5https://richie.education/docs/2.7.1/frontend-overridesweekly0.5https://richie.education/docs/2.7.1/internationalizationweekly0.5https://richie.education/docs/2.7.1/lms-connectionweekly0.5https://richie.education/docs/2.7.1/native-installationweekly0.5https://richie.education/docs/2.8.0/accessibility-testingweekly0.5https://richie.education/docs/2.8.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.8.0/building-the-frontendweekly0.5https://richie.education/docs/2.8.0/contributing-guideweekly0.5https://richie.education/docs/2.8.0/css-guidelinesweekly0.5https://richie.education/docs/2.8.0/discoverweekly0.5https://richie.education/docs/2.8.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.8.0/django-react-interopweekly0.5https://richie.education/docs/2.8.0/docker-developmentweekly0.5https://richie.education/docs/2.8.0/frontend-overridesweekly0.5https://richie.education/docs/2.8.0/internationalizationweekly0.5https://richie.education/docs/2.8.0/lms-backendsweekly0.5https://richie.education/docs/2.8.0/lms-connectionweekly0.5https://richie.education/docs/2.8.0/native-installationweekly0.5https://richie.education/docs/2.8.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.8.0/tls-connectionweekly0.5https://richie.education/docs/2.8.0/web-analyticsweekly0.5https://richie.education/docs/2.8.1/accessibility-testingweekly0.5https://richie.education/docs/2.8.1/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.8.1/building-the-frontendweekly0.5https://richie.education/docs/2.8.1/contributing-guideweekly0.5https://richie.education/docs/2.8.1/css-guidelinesweekly0.5https://richie.education/docs/2.8.1/discoverweekly0.5https://richie.education/docs/2.8.1/displaying-connection-statusweekly0.5https://richie.education/docs/2.8.1/django-react-interopweekly0.5https://richie.education/docs/2.8.1/docker-developmentweekly0.5https://richie.education/docs/2.8.1/frontend-overridesweekly0.5https://richie.education/docs/2.8.1/internationalizationweekly0.5https://richie.education/docs/2.8.1/lms-backendsweekly0.5https://richie.education/docs/2.8.1/lms-connectionweekly0.5https://richie.education/docs/2.8.1/native-installationweekly0.5https://richie.education/docs/2.8.1/synchronizing-course-runsweekly0.5https://richie.education/docs/2.8.1/tls-connectionweekly0.5https://richie.education/docs/2.8.1/web-analyticsweekly0.5https://richie.education/docs/2.8.2/accessibility-testingweekly0.5https://richie.education/docs/2.8.2/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.8.2/building-the-frontendweekly0.5https://richie.education/docs/2.8.2/contributing-guideweekly0.5https://richie.education/docs/2.8.2/css-guidelinesweekly0.5https://richie.education/docs/2.8.2/discoverweekly0.5https://richie.education/docs/2.8.2/displaying-connection-statusweekly0.5https://richie.education/docs/2.8.2/django-react-interopweekly0.5https://richie.education/docs/2.8.2/docker-developmentweekly0.5https://richie.education/docs/2.8.2/frontend-overridesweekly0.5https://richie.education/docs/2.8.2/internationalizationweekly0.5https://richie.education/docs/2.8.2/lms-backendsweekly0.5https://richie.education/docs/2.8.2/lms-connectionweekly0.5https://richie.education/docs/2.8.2/native-installationweekly0.5https://richie.education/docs/2.8.2/synchronizing-course-runsweekly0.5https://richie.education/docs/2.8.2/tls-connectionweekly0.5https://richie.education/docs/2.8.2/web-analyticsweekly0.5https://richie.education/docs/2.9.0/accessibility-testingweekly0.5https://richie.education/docs/2.9.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.9.0/building-the-frontendweekly0.5https://richie.education/docs/2.9.0/contributing-guideweekly0.5https://richie.education/docs/2.9.0/css-guidelinesweekly0.5https://richie.education/docs/2.9.0/discoverweekly0.5https://richie.education/docs/2.9.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.9.0/django-react-interopweekly0.5https://richie.education/docs/2.9.0/docker-developmentweekly0.5https://richie.education/docs/2.9.0/frontend-overridesweekly0.5https://richie.education/docs/2.9.0/internationalizationweekly0.5https://richie.education/docs/2.9.0/lms-backendsweekly0.5https://richie.education/docs/2.9.0/lms-connectionweekly0.5https://richie.education/docs/2.9.0/native-installationweekly0.5https://richie.education/docs/2.9.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.9.0/tls-connectionweekly0.5https://richie.education/docs/2.9.0/web-analyticsweekly0.5https://richie.education/docs/2.9.1/accessibility-testingweekly0.5https://richie.education/docs/2.9.1/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.9.1/building-the-frontendweekly0.5https://richie.education/docs/2.9.1/contributing-guideweekly0.5https://richie.education/docs/2.9.1/css-guidelinesweekly0.5https://richie.education/docs/2.9.1/discoverweekly0.5https://richie.education/docs/2.9.1/displaying-connection-statusweekly0.5https://richie.education/docs/2.9.1/django-react-interopweekly0.5https://richie.education/docs/2.9.1/docker-developmentweekly0.5https://richie.education/docs/2.9.1/frontend-overridesweekly0.5https://richie.education/docs/2.9.1/internationalizationweekly0.5https://richie.education/docs/2.9.1/lms-backendsweekly0.5https://richie.education/docs/2.9.1/lms-connectionweekly0.5https://richie.education/docs/2.9.1/native-installationweekly0.5https://richie.education/docs/2.9.1/synchronizing-course-runsweekly0.5https://richie.education/docs/2.9.1/tls-connectionweekly0.5https://richie.education/docs/2.9.1/web-analyticsweekly0.5https://richie.education/docs/next/accessibility-testingweekly0.5https://richie.education/docs/next/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/next/building-the-frontendweekly0.5https://richie.education/docs/next/contributing-guideweekly0.5https://richie.education/docs/next/cookiecutterweekly0.5https://richie.education/docs/next/css-guidelinesweekly0.5https://richie.education/docs/next/discoverweekly0.5https://richie.education/docs/next/displaying-connection-statusweekly0.5https://richie.education/docs/next/django-react-interopweekly0.5https://richie.education/docs/next/docker-developmentweekly0.5https://richie.education/docs/next/filters-customizationweekly0.5https://richie.education/docs/next/frontend-overridesweekly0.5https://richie.education/docs/next/installationweekly0.5https://richie.education/docs/next/internationalizationweekly0.5https://richie.education/docs/next/joanie-connectionweekly0.5https://richie.education/docs/next/lms-backendsweekly0.5https://richie.education/docs/next/lms-connectionweekly0.5https://richie.education/docs/next/native-installationweekly0.5https://richie.education/docs/next/synchronizing-course-runsweekly0.5https://richie.education/docs/next/tls-connectionweekly0.5https://richie.education/docs/next/web-analyticsweekly0.5https://richie.education/docs/accessibility-testingweekly0.5https://richie.education/docs/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/building-the-frontendweekly0.5https://richie.education/docs/contributing-guideweekly0.5https://richie.education/docs/cookiecutterweekly0.5https://richie.education/docs/css-guidelinesweekly0.5https://richie.education/docs/discoverweekly0.5https://richie.education/docs/displaying-connection-statusweekly0.5https://richie.education/docs/django-react-interopweekly0.5https://richie.education/docs/docker-developmentweekly0.5https://richie.education/docs/filters-customizationweekly0.5https://richie.education/docs/frontend-overridesweekly0.5https://richie.education/docs/installationweekly0.5https://richie.education/docs/internationalizationweekly0.5https://richie.education/docs/joanie-connectionweekly0.5https://richie.education/docs/lms-backendsweekly0.5https://richie.education/docs/lms-connectionweekly0.5https://richie.education/docs/native-installationweekly0.5https://richie.education/docs/synchronizing-course-runsweekly0.5https://richie.education/docs/tls-connectionweekly0.5https://richie.education/docs/web-analyticsweekly0.5https://richie.education/weekly0.5 \ No newline at end of file +https://richie.education/helpweekly0.5https://richie.education/usersweekly0.5https://richie.education/versionsweekly0.5https://richie.education/docs/1.12/contributing-guideweekly0.5https://richie.education/docs/1.12/css-guidelinesweekly0.5https://richie.education/docs/1.12/discoverweekly0.5https://richie.education/docs/1.12/django-react-interopweekly0.5https://richie.education/docs/1.12/docker-developmentweekly0.5https://richie.education/docs/1.12/native-installationweekly0.5https://richie.education/docs/1.13/building-the-frontendweekly0.5https://richie.education/docs/1.13/contributing-guideweekly0.5https://richie.education/docs/1.13/css-guidelinesweekly0.5https://richie.education/docs/1.13/discoverweekly0.5https://richie.education/docs/1.13/django-react-interopweekly0.5https://richie.education/docs/1.13/docker-developmentweekly0.5https://richie.education/docs/1.13/native-installationweekly0.5https://richie.education/docs/1.14/building-the-frontendweekly0.5https://richie.education/docs/1.14/contributing-guideweekly0.5https://richie.education/docs/1.14/css-guidelinesweekly0.5https://richie.education/docs/1.14/discoverweekly0.5https://richie.education/docs/1.14/django-react-interopweekly0.5https://richie.education/docs/1.14/docker-developmentweekly0.5https://richie.education/docs/1.14/native-installationweekly0.5https://richie.education/docs/1.15/building-the-frontendweekly0.5https://richie.education/docs/1.15/contributing-guideweekly0.5https://richie.education/docs/1.15/css-guidelinesweekly0.5https://richie.education/docs/1.15/discoverweekly0.5https://richie.education/docs/1.15/django-react-interopweekly0.5https://richie.education/docs/1.15/docker-developmentweekly0.5https://richie.education/docs/1.15/native-installationweekly0.5https://richie.education/docs/1.16/accessibility-testingweekly0.5https://richie.education/docs/1.16/building-the-frontendweekly0.5https://richie.education/docs/1.16/contributing-guideweekly0.5https://richie.education/docs/1.16/css-guidelinesweekly0.5https://richie.education/docs/1.16/discoverweekly0.5https://richie.education/docs/1.16/django-react-interopweekly0.5https://richie.education/docs/1.16/docker-developmentweekly0.5https://richie.education/docs/1.16/native-installationweekly0.5https://richie.education/docs/1.17/accessibility-testingweekly0.5https://richie.education/docs/1.17/building-the-frontendweekly0.5https://richie.education/docs/1.17/contributing-guideweekly0.5https://richie.education/docs/1.17/css-guidelinesweekly0.5https://richie.education/docs/1.17/discoverweekly0.5https://richie.education/docs/1.17/django-react-interopweekly0.5https://richie.education/docs/1.17/docker-developmentweekly0.5https://richie.education/docs/1.17/native-installationweekly0.5https://richie.education/docs/2.0.0/accessibility-testingweekly0.5https://richie.education/docs/2.0.0/building-the-frontendweekly0.5https://richie.education/docs/2.0.0/contributing-guideweekly0.5https://richie.education/docs/2.0.0/css-guidelinesweekly0.5https://richie.education/docs/2.0.0/discoverweekly0.5https://richie.education/docs/2.0.0/django-react-interopweekly0.5https://richie.education/docs/2.0.0/docker-developmentweekly0.5https://richie.education/docs/2.0.0/frontend-overridesweekly0.5https://richie.education/docs/2.0.0/lms-connectionweekly0.5https://richie.education/docs/2.0.0/native-installationweekly0.5https://richie.education/docs/2.0.1/accessibility-testingweekly0.5https://richie.education/docs/2.0.1/building-the-frontendweekly0.5https://richie.education/docs/2.0.1/contributing-guideweekly0.5https://richie.education/docs/2.0.1/css-guidelinesweekly0.5https://richie.education/docs/2.0.1/discoverweekly0.5https://richie.education/docs/2.0.1/django-react-interopweekly0.5https://richie.education/docs/2.0.1/docker-developmentweekly0.5https://richie.education/docs/2.0.1/frontend-overridesweekly0.5https://richie.education/docs/2.0.1/lms-connectionweekly0.5https://richie.education/docs/2.0.1/native-installationweekly0.5https://richie.education/docs/2.1.0/accessibility-testingweekly0.5https://richie.education/docs/2.1.0/building-the-frontendweekly0.5https://richie.education/docs/2.1.0/contributing-guideweekly0.5https://richie.education/docs/2.1.0/css-guidelinesweekly0.5https://richie.education/docs/2.1.0/discoverweekly0.5https://richie.education/docs/2.1.0/django-react-interopweekly0.5https://richie.education/docs/2.1.0/docker-developmentweekly0.5https://richie.education/docs/2.1.0/frontend-overridesweekly0.5https://richie.education/docs/2.1.0/lms-connectionweekly0.5https://richie.education/docs/2.1.0/native-installationweekly0.5https://richie.education/docs/2.10.0/accessibility-testingweekly0.5https://richie.education/docs/2.10.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.10.0/building-the-frontendweekly0.5https://richie.education/docs/2.10.0/contributing-guideweekly0.5https://richie.education/docs/2.10.0/css-guidelinesweekly0.5https://richie.education/docs/2.10.0/discoverweekly0.5https://richie.education/docs/2.10.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.10.0/django-react-interopweekly0.5https://richie.education/docs/2.10.0/docker-developmentweekly0.5https://richie.education/docs/2.10.0/frontend-overridesweekly0.5https://richie.education/docs/2.10.0/internationalizationweekly0.5https://richie.education/docs/2.10.0/lms-backendsweekly0.5https://richie.education/docs/2.10.0/lms-connectionweekly0.5https://richie.education/docs/2.10.0/native-installationweekly0.5https://richie.education/docs/2.10.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.10.0/tls-connectionweekly0.5https://richie.education/docs/2.10.0/web-analyticsweekly0.5https://richie.education/docs/2.11.0/accessibility-testingweekly0.5https://richie.education/docs/2.11.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.11.0/building-the-frontendweekly0.5https://richie.education/docs/2.11.0/contributing-guideweekly0.5https://richie.education/docs/2.11.0/css-guidelinesweekly0.5https://richie.education/docs/2.11.0/discoverweekly0.5https://richie.education/docs/2.11.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.11.0/django-react-interopweekly0.5https://richie.education/docs/2.11.0/docker-developmentweekly0.5https://richie.education/docs/2.11.0/frontend-overridesweekly0.5https://richie.education/docs/2.11.0/internationalizationweekly0.5https://richie.education/docs/2.11.0/lms-backendsweekly0.5https://richie.education/docs/2.11.0/lms-connectionweekly0.5https://richie.education/docs/2.11.0/native-installationweekly0.5https://richie.education/docs/2.11.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.11.0/tls-connectionweekly0.5https://richie.education/docs/2.11.0/web-analyticsweekly0.5https://richie.education/docs/2.12.0/accessibility-testingweekly0.5https://richie.education/docs/2.12.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.12.0/building-the-frontendweekly0.5https://richie.education/docs/2.12.0/contributing-guideweekly0.5https://richie.education/docs/2.12.0/css-guidelinesweekly0.5https://richie.education/docs/2.12.0/discoverweekly0.5https://richie.education/docs/2.12.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.12.0/django-react-interopweekly0.5https://richie.education/docs/2.12.0/docker-developmentweekly0.5https://richie.education/docs/2.12.0/frontend-overridesweekly0.5https://richie.education/docs/2.12.0/internationalizationweekly0.5https://richie.education/docs/2.12.0/lms-backendsweekly0.5https://richie.education/docs/2.12.0/lms-connectionweekly0.5https://richie.education/docs/2.12.0/native-installationweekly0.5https://richie.education/docs/2.12.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.12.0/tls-connectionweekly0.5https://richie.education/docs/2.12.0/web-analyticsweekly0.5https://richie.education/docs/2.13.0/accessibility-testingweekly0.5https://richie.education/docs/2.13.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.13.0/building-the-frontendweekly0.5https://richie.education/docs/2.13.0/contributing-guideweekly0.5https://richie.education/docs/2.13.0/css-guidelinesweekly0.5https://richie.education/docs/2.13.0/discoverweekly0.5https://richie.education/docs/2.13.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.13.0/django-react-interopweekly0.5https://richie.education/docs/2.13.0/docker-developmentweekly0.5https://richie.education/docs/2.13.0/frontend-overridesweekly0.5https://richie.education/docs/2.13.0/internationalizationweekly0.5https://richie.education/docs/2.13.0/lms-backendsweekly0.5https://richie.education/docs/2.13.0/lms-connectionweekly0.5https://richie.education/docs/2.13.0/native-installationweekly0.5https://richie.education/docs/2.13.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.13.0/tls-connectionweekly0.5https://richie.education/docs/2.13.0/web-analyticsweekly0.5https://richie.education/docs/2.14.0/accessibility-testingweekly0.5https://richie.education/docs/2.14.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.14.0/building-the-frontendweekly0.5https://richie.education/docs/2.14.0/contributing-guideweekly0.5https://richie.education/docs/2.14.0/css-guidelinesweekly0.5https://richie.education/docs/2.14.0/discoverweekly0.5https://richie.education/docs/2.14.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.14.0/django-react-interopweekly0.5https://richie.education/docs/2.14.0/docker-developmentweekly0.5https://richie.education/docs/2.14.0/frontend-overridesweekly0.5https://richie.education/docs/2.14.0/internationalizationweekly0.5https://richie.education/docs/2.14.0/lms-backendsweekly0.5https://richie.education/docs/2.14.0/lms-connectionweekly0.5https://richie.education/docs/2.14.0/native-installationweekly0.5https://richie.education/docs/2.14.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.14.0/tls-connectionweekly0.5https://richie.education/docs/2.14.0/web-analyticsweekly0.5https://richie.education/docs/2.14.1/accessibility-testingweekly0.5https://richie.education/docs/2.14.1/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.14.1/building-the-frontendweekly0.5https://richie.education/docs/2.14.1/contributing-guideweekly0.5https://richie.education/docs/2.14.1/cookiecutterweekly0.5https://richie.education/docs/2.14.1/css-guidelinesweekly0.5https://richie.education/docs/2.14.1/discoverweekly0.5https://richie.education/docs/2.14.1/displaying-connection-statusweekly0.5https://richie.education/docs/2.14.1/django-react-interopweekly0.5https://richie.education/docs/2.14.1/docker-developmentweekly0.5https://richie.education/docs/2.14.1/frontend-overridesweekly0.5https://richie.education/docs/2.14.1/installationweekly0.5https://richie.education/docs/2.14.1/internationalizationweekly0.5https://richie.education/docs/2.14.1/joanie-connectionweekly0.5https://richie.education/docs/2.14.1/lms-backendsweekly0.5https://richie.education/docs/2.14.1/lms-connectionweekly0.5https://richie.education/docs/2.14.1/native-installationweekly0.5https://richie.education/docs/2.14.1/synchronizing-course-runsweekly0.5https://richie.education/docs/2.14.1/tls-connectionweekly0.5https://richie.education/docs/2.14.1/web-analyticsweekly0.5https://richie.education/docs/2.15.0/accessibility-testingweekly0.5https://richie.education/docs/2.15.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.15.0/building-the-frontendweekly0.5https://richie.education/docs/2.15.0/contributing-guideweekly0.5https://richie.education/docs/2.15.0/cookiecutterweekly0.5https://richie.education/docs/2.15.0/css-guidelinesweekly0.5https://richie.education/docs/2.15.0/discoverweekly0.5https://richie.education/docs/2.15.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.15.0/django-react-interopweekly0.5https://richie.education/docs/2.15.0/docker-developmentweekly0.5https://richie.education/docs/2.15.0/frontend-overridesweekly0.5https://richie.education/docs/2.15.0/installationweekly0.5https://richie.education/docs/2.15.0/internationalizationweekly0.5https://richie.education/docs/2.15.0/joanie-connectionweekly0.5https://richie.education/docs/2.15.0/lms-backendsweekly0.5https://richie.education/docs/2.15.0/lms-connectionweekly0.5https://richie.education/docs/2.15.0/native-installationweekly0.5https://richie.education/docs/2.15.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.15.0/tls-connectionweekly0.5https://richie.education/docs/2.15.0/web-analyticsweekly0.5https://richie.education/docs/2.15.1/accessibility-testingweekly0.5https://richie.education/docs/2.15.1/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.15.1/building-the-frontendweekly0.5https://richie.education/docs/2.15.1/contributing-guideweekly0.5https://richie.education/docs/2.15.1/cookiecutterweekly0.5https://richie.education/docs/2.15.1/css-guidelinesweekly0.5https://richie.education/docs/2.15.1/discoverweekly0.5https://richie.education/docs/2.15.1/displaying-connection-statusweekly0.5https://richie.education/docs/2.15.1/django-react-interopweekly0.5https://richie.education/docs/2.15.1/docker-developmentweekly0.5https://richie.education/docs/2.15.1/frontend-overridesweekly0.5https://richie.education/docs/2.15.1/installationweekly0.5https://richie.education/docs/2.15.1/internationalizationweekly0.5https://richie.education/docs/2.15.1/joanie-connectionweekly0.5https://richie.education/docs/2.15.1/lms-backendsweekly0.5https://richie.education/docs/2.15.1/lms-connectionweekly0.5https://richie.education/docs/2.15.1/native-installationweekly0.5https://richie.education/docs/2.15.1/synchronizing-course-runsweekly0.5https://richie.education/docs/2.15.1/tls-connectionweekly0.5https://richie.education/docs/2.15.1/web-analyticsweekly0.5https://richie.education/docs/2.16.0/accessibility-testingweekly0.5https://richie.education/docs/2.16.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.16.0/building-the-frontendweekly0.5https://richie.education/docs/2.16.0/contributing-guideweekly0.5https://richie.education/docs/2.16.0/cookiecutterweekly0.5https://richie.education/docs/2.16.0/css-guidelinesweekly0.5https://richie.education/docs/2.16.0/discoverweekly0.5https://richie.education/docs/2.16.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.16.0/django-react-interopweekly0.5https://richie.education/docs/2.16.0/docker-developmentweekly0.5https://richie.education/docs/2.16.0/frontend-overridesweekly0.5https://richie.education/docs/2.16.0/installationweekly0.5https://richie.education/docs/2.16.0/internationalizationweekly0.5https://richie.education/docs/2.16.0/joanie-connectionweekly0.5https://richie.education/docs/2.16.0/lms-backendsweekly0.5https://richie.education/docs/2.16.0/lms-connectionweekly0.5https://richie.education/docs/2.16.0/native-installationweekly0.5https://richie.education/docs/2.16.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.16.0/tls-connectionweekly0.5https://richie.education/docs/2.16.0/web-analyticsweekly0.5https://richie.education/docs/2.17.0/accessibility-testingweekly0.5https://richie.education/docs/2.17.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.17.0/building-the-frontendweekly0.5https://richie.education/docs/2.17.0/contributing-guideweekly0.5https://richie.education/docs/2.17.0/cookiecutterweekly0.5https://richie.education/docs/2.17.0/css-guidelinesweekly0.5https://richie.education/docs/2.17.0/discoverweekly0.5https://richie.education/docs/2.17.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.17.0/django-react-interopweekly0.5https://richie.education/docs/2.17.0/docker-developmentweekly0.5https://richie.education/docs/2.17.0/filters-customizationweekly0.5https://richie.education/docs/2.17.0/frontend-overridesweekly0.5https://richie.education/docs/2.17.0/installationweekly0.5https://richie.education/docs/2.17.0/internationalizationweekly0.5https://richie.education/docs/2.17.0/joanie-connectionweekly0.5https://richie.education/docs/2.17.0/lms-backendsweekly0.5https://richie.education/docs/2.17.0/lms-connectionweekly0.5https://richie.education/docs/2.17.0/native-installationweekly0.5https://richie.education/docs/2.17.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.17.0/tls-connectionweekly0.5https://richie.education/docs/2.17.0/web-analyticsweekly0.5https://richie.education/docs/2.18.0/accessibility-testingweekly0.5https://richie.education/docs/2.18.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.18.0/building-the-frontendweekly0.5https://richie.education/docs/2.18.0/contributing-guideweekly0.5https://richie.education/docs/2.18.0/cookiecutterweekly0.5https://richie.education/docs/2.18.0/css-guidelinesweekly0.5https://richie.education/docs/2.18.0/discoverweekly0.5https://richie.education/docs/2.18.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.18.0/django-react-interopweekly0.5https://richie.education/docs/2.18.0/docker-developmentweekly0.5https://richie.education/docs/2.18.0/filters-customizationweekly0.5https://richie.education/docs/2.18.0/frontend-overridesweekly0.5https://richie.education/docs/2.18.0/installationweekly0.5https://richie.education/docs/2.18.0/internationalizationweekly0.5https://richie.education/docs/2.18.0/joanie-connectionweekly0.5https://richie.education/docs/2.18.0/lms-backendsweekly0.5https://richie.education/docs/2.18.0/lms-connectionweekly0.5https://richie.education/docs/2.18.0/native-installationweekly0.5https://richie.education/docs/2.18.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.18.0/tls-connectionweekly0.5https://richie.education/docs/2.18.0/web-analyticsweekly0.5https://richie.education/docs/2.19.0/accessibility-testingweekly0.5https://richie.education/docs/2.19.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.19.0/building-the-frontendweekly0.5https://richie.education/docs/2.19.0/contributing-guideweekly0.5https://richie.education/docs/2.19.0/cookiecutterweekly0.5https://richie.education/docs/2.19.0/css-guidelinesweekly0.5https://richie.education/docs/2.19.0/discoverweekly0.5https://richie.education/docs/2.19.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.19.0/django-react-interopweekly0.5https://richie.education/docs/2.19.0/docker-developmentweekly0.5https://richie.education/docs/2.19.0/filters-customizationweekly0.5https://richie.education/docs/2.19.0/frontend-overridesweekly0.5https://richie.education/docs/2.19.0/installationweekly0.5https://richie.education/docs/2.19.0/internationalizationweekly0.5https://richie.education/docs/2.19.0/joanie-connectionweekly0.5https://richie.education/docs/2.19.0/lms-backendsweekly0.5https://richie.education/docs/2.19.0/lms-connectionweekly0.5https://richie.education/docs/2.19.0/native-installationweekly0.5https://richie.education/docs/2.19.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.19.0/tls-connectionweekly0.5https://richie.education/docs/2.19.0/web-analyticsweekly0.5https://richie.education/docs/2.2.0/accessibility-testingweekly0.5https://richie.education/docs/2.2.0/building-the-frontendweekly0.5https://richie.education/docs/2.2.0/contributing-guideweekly0.5https://richie.education/docs/2.2.0/css-guidelinesweekly0.5https://richie.education/docs/2.2.0/discoverweekly0.5https://richie.education/docs/2.2.0/django-react-interopweekly0.5https://richie.education/docs/2.2.0/docker-developmentweekly0.5https://richie.education/docs/2.2.0/frontend-overridesweekly0.5https://richie.education/docs/2.2.0/internationalizationweekly0.5https://richie.education/docs/2.2.0/lms-connectionweekly0.5https://richie.education/docs/2.2.0/native-installationweekly0.5https://richie.education/docs/2.20.0/accessibility-testingweekly0.5https://richie.education/docs/2.20.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.20.0/building-the-frontendweekly0.5https://richie.education/docs/2.20.0/contributing-guideweekly0.5https://richie.education/docs/2.20.0/cookiecutterweekly0.5https://richie.education/docs/2.20.0/css-guidelinesweekly0.5https://richie.education/docs/2.20.0/discoverweekly0.5https://richie.education/docs/2.20.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.20.0/django-react-interopweekly0.5https://richie.education/docs/2.20.0/docker-developmentweekly0.5https://richie.education/docs/2.20.0/filters-customizationweekly0.5https://richie.education/docs/2.20.0/frontend-overridesweekly0.5https://richie.education/docs/2.20.0/installationweekly0.5https://richie.education/docs/2.20.0/internationalizationweekly0.5https://richie.education/docs/2.20.0/joanie-connectionweekly0.5https://richie.education/docs/2.20.0/lms-backendsweekly0.5https://richie.education/docs/2.20.0/lms-connectionweekly0.5https://richie.education/docs/2.20.0/native-installationweekly0.5https://richie.education/docs/2.20.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.20.0/tls-connectionweekly0.5https://richie.education/docs/2.20.0/web-analyticsweekly0.5https://richie.education/docs/2.20.1/accessibility-testingweekly0.5https://richie.education/docs/2.20.1/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.20.1/building-the-frontendweekly0.5https://richie.education/docs/2.20.1/contributing-guideweekly0.5https://richie.education/docs/2.20.1/cookiecutterweekly0.5https://richie.education/docs/2.20.1/css-guidelinesweekly0.5https://richie.education/docs/2.20.1/discoverweekly0.5https://richie.education/docs/2.20.1/displaying-connection-statusweekly0.5https://richie.education/docs/2.20.1/django-react-interopweekly0.5https://richie.education/docs/2.20.1/docker-developmentweekly0.5https://richie.education/docs/2.20.1/filters-customizationweekly0.5https://richie.education/docs/2.20.1/frontend-overridesweekly0.5https://richie.education/docs/2.20.1/installationweekly0.5https://richie.education/docs/2.20.1/internationalizationweekly0.5https://richie.education/docs/2.20.1/joanie-connectionweekly0.5https://richie.education/docs/2.20.1/lms-backendsweekly0.5https://richie.education/docs/2.20.1/lms-connectionweekly0.5https://richie.education/docs/2.20.1/native-installationweekly0.5https://richie.education/docs/2.20.1/synchronizing-course-runsweekly0.5https://richie.education/docs/2.20.1/tls-connectionweekly0.5https://richie.education/docs/2.20.1/web-analyticsweekly0.5https://richie.education/docs/2.21.0/accessibility-testingweekly0.5https://richie.education/docs/2.21.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.21.0/building-the-frontendweekly0.5https://richie.education/docs/2.21.0/contributing-guideweekly0.5https://richie.education/docs/2.21.0/cookiecutterweekly0.5https://richie.education/docs/2.21.0/css-guidelinesweekly0.5https://richie.education/docs/2.21.0/discoverweekly0.5https://richie.education/docs/2.21.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.21.0/django-react-interopweekly0.5https://richie.education/docs/2.21.0/docker-developmentweekly0.5https://richie.education/docs/2.21.0/filters-customizationweekly0.5https://richie.education/docs/2.21.0/frontend-overridesweekly0.5https://richie.education/docs/2.21.0/installationweekly0.5https://richie.education/docs/2.21.0/internationalizationweekly0.5https://richie.education/docs/2.21.0/joanie-connectionweekly0.5https://richie.education/docs/2.21.0/lms-backendsweekly0.5https://richie.education/docs/2.21.0/lms-connectionweekly0.5https://richie.education/docs/2.21.0/native-installationweekly0.5https://richie.education/docs/2.21.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.21.0/tls-connectionweekly0.5https://richie.education/docs/2.21.0/web-analyticsweekly0.5https://richie.education/docs/2.21.1/accessibility-testingweekly0.5https://richie.education/docs/2.21.1/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.21.1/building-the-frontendweekly0.5https://richie.education/docs/2.21.1/contributing-guideweekly0.5https://richie.education/docs/2.21.1/cookiecutterweekly0.5https://richie.education/docs/2.21.1/css-guidelinesweekly0.5https://richie.education/docs/2.21.1/discoverweekly0.5https://richie.education/docs/2.21.1/displaying-connection-statusweekly0.5https://richie.education/docs/2.21.1/django-react-interopweekly0.5https://richie.education/docs/2.21.1/docker-developmentweekly0.5https://richie.education/docs/2.21.1/filters-customizationweekly0.5https://richie.education/docs/2.21.1/frontend-overridesweekly0.5https://richie.education/docs/2.21.1/installationweekly0.5https://richie.education/docs/2.21.1/internationalizationweekly0.5https://richie.education/docs/2.21.1/joanie-connectionweekly0.5https://richie.education/docs/2.21.1/lms-backendsweekly0.5https://richie.education/docs/2.21.1/lms-connectionweekly0.5https://richie.education/docs/2.21.1/native-installationweekly0.5https://richie.education/docs/2.21.1/synchronizing-course-runsweekly0.5https://richie.education/docs/2.21.1/tls-connectionweekly0.5https://richie.education/docs/2.21.1/web-analyticsweekly0.5https://richie.education/docs/2.3.0/accessibility-testingweekly0.5https://richie.education/docs/2.3.0/building-the-frontendweekly0.5https://richie.education/docs/2.3.0/contributing-guideweekly0.5https://richie.education/docs/2.3.0/css-guidelinesweekly0.5https://richie.education/docs/2.3.0/discoverweekly0.5https://richie.education/docs/2.3.0/django-react-interopweekly0.5https://richie.education/docs/2.3.0/docker-developmentweekly0.5https://richie.education/docs/2.3.0/frontend-overridesweekly0.5https://richie.education/docs/2.3.0/internationalizationweekly0.5https://richie.education/docs/2.3.0/lms-connectionweekly0.5https://richie.education/docs/2.3.0/native-installationweekly0.5https://richie.education/docs/2.3.1/accessibility-testingweekly0.5https://richie.education/docs/2.3.1/building-the-frontendweekly0.5https://richie.education/docs/2.3.1/contributing-guideweekly0.5https://richie.education/docs/2.3.1/css-guidelinesweekly0.5https://richie.education/docs/2.3.1/discoverweekly0.5https://richie.education/docs/2.3.1/django-react-interopweekly0.5https://richie.education/docs/2.3.1/docker-developmentweekly0.5https://richie.education/docs/2.3.1/frontend-overridesweekly0.5https://richie.education/docs/2.3.1/internationalizationweekly0.5https://richie.education/docs/2.3.1/lms-connectionweekly0.5https://richie.education/docs/2.3.1/native-installationweekly0.5https://richie.education/docs/2.3.2/accessibility-testingweekly0.5https://richie.education/docs/2.3.2/building-the-frontendweekly0.5https://richie.education/docs/2.3.2/contributing-guideweekly0.5https://richie.education/docs/2.3.2/css-guidelinesweekly0.5https://richie.education/docs/2.3.2/discoverweekly0.5https://richie.education/docs/2.3.2/django-react-interopweekly0.5https://richie.education/docs/2.3.2/docker-developmentweekly0.5https://richie.education/docs/2.3.2/frontend-overridesweekly0.5https://richie.education/docs/2.3.2/internationalizationweekly0.5https://richie.education/docs/2.3.2/lms-connectionweekly0.5https://richie.education/docs/2.3.2/native-installationweekly0.5https://richie.education/docs/2.3.3/accessibility-testingweekly0.5https://richie.education/docs/2.3.3/building-the-frontendweekly0.5https://richie.education/docs/2.3.3/contributing-guideweekly0.5https://richie.education/docs/2.3.3/css-guidelinesweekly0.5https://richie.education/docs/2.3.3/discoverweekly0.5https://richie.education/docs/2.3.3/django-react-interopweekly0.5https://richie.education/docs/2.3.3/docker-developmentweekly0.5https://richie.education/docs/2.3.3/frontend-overridesweekly0.5https://richie.education/docs/2.3.3/internationalizationweekly0.5https://richie.education/docs/2.3.3/lms-connectionweekly0.5https://richie.education/docs/2.3.3/native-installationweekly0.5https://richie.education/docs/2.4.0/accessibility-testingweekly0.5https://richie.education/docs/2.4.0/building-the-frontendweekly0.5https://richie.education/docs/2.4.0/contributing-guideweekly0.5https://richie.education/docs/2.4.0/css-guidelinesweekly0.5https://richie.education/docs/2.4.0/discoverweekly0.5https://richie.education/docs/2.4.0/django-react-interopweekly0.5https://richie.education/docs/2.4.0/docker-developmentweekly0.5https://richie.education/docs/2.4.0/frontend-overridesweekly0.5https://richie.education/docs/2.4.0/internationalizationweekly0.5https://richie.education/docs/2.4.0/lms-connectionweekly0.5https://richie.education/docs/2.4.0/native-installationweekly0.5https://richie.education/docs/2.5.0/accessibility-testingweekly0.5https://richie.education/docs/2.5.0/building-the-frontendweekly0.5https://richie.education/docs/2.5.0/contributing-guideweekly0.5https://richie.education/docs/2.5.0/css-guidelinesweekly0.5https://richie.education/docs/2.5.0/discoverweekly0.5https://richie.education/docs/2.5.0/django-react-interopweekly0.5https://richie.education/docs/2.5.0/docker-developmentweekly0.5https://richie.education/docs/2.5.0/frontend-overridesweekly0.5https://richie.education/docs/2.5.0/internationalizationweekly0.5https://richie.education/docs/2.5.0/lms-connectionweekly0.5https://richie.education/docs/2.5.0/native-installationweekly0.5https://richie.education/docs/2.6.0/accessibility-testingweekly0.5https://richie.education/docs/2.6.0/building-the-frontendweekly0.5https://richie.education/docs/2.6.0/contributing-guideweekly0.5https://richie.education/docs/2.6.0/css-guidelinesweekly0.5https://richie.education/docs/2.6.0/discoverweekly0.5https://richie.education/docs/2.6.0/django-react-interopweekly0.5https://richie.education/docs/2.6.0/docker-developmentweekly0.5https://richie.education/docs/2.6.0/frontend-overridesweekly0.5https://richie.education/docs/2.6.0/internationalizationweekly0.5https://richie.education/docs/2.6.0/lms-connectionweekly0.5https://richie.education/docs/2.6.0/native-installationweekly0.5https://richie.education/docs/2.7.0/accessibility-testingweekly0.5https://richie.education/docs/2.7.0/building-the-frontendweekly0.5https://richie.education/docs/2.7.0/contributing-guideweekly0.5https://richie.education/docs/2.7.0/css-guidelinesweekly0.5https://richie.education/docs/2.7.0/discoverweekly0.5https://richie.education/docs/2.7.0/django-react-interopweekly0.5https://richie.education/docs/2.7.0/docker-developmentweekly0.5https://richie.education/docs/2.7.0/frontend-overridesweekly0.5https://richie.education/docs/2.7.0/internationalizationweekly0.5https://richie.education/docs/2.7.0/lms-connectionweekly0.5https://richie.education/docs/2.7.0/native-installationweekly0.5https://richie.education/docs/2.7.1/accessibility-testingweekly0.5https://richie.education/docs/2.7.1/building-the-frontendweekly0.5https://richie.education/docs/2.7.1/contributing-guideweekly0.5https://richie.education/docs/2.7.1/css-guidelinesweekly0.5https://richie.education/docs/2.7.1/discoverweekly0.5https://richie.education/docs/2.7.1/django-react-interopweekly0.5https://richie.education/docs/2.7.1/docker-developmentweekly0.5https://richie.education/docs/2.7.1/frontend-overridesweekly0.5https://richie.education/docs/2.7.1/internationalizationweekly0.5https://richie.education/docs/2.7.1/lms-connectionweekly0.5https://richie.education/docs/2.7.1/native-installationweekly0.5https://richie.education/docs/2.8.0/accessibility-testingweekly0.5https://richie.education/docs/2.8.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.8.0/building-the-frontendweekly0.5https://richie.education/docs/2.8.0/contributing-guideweekly0.5https://richie.education/docs/2.8.0/css-guidelinesweekly0.5https://richie.education/docs/2.8.0/discoverweekly0.5https://richie.education/docs/2.8.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.8.0/django-react-interopweekly0.5https://richie.education/docs/2.8.0/docker-developmentweekly0.5https://richie.education/docs/2.8.0/frontend-overridesweekly0.5https://richie.education/docs/2.8.0/internationalizationweekly0.5https://richie.education/docs/2.8.0/lms-backendsweekly0.5https://richie.education/docs/2.8.0/lms-connectionweekly0.5https://richie.education/docs/2.8.0/native-installationweekly0.5https://richie.education/docs/2.8.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.8.0/tls-connectionweekly0.5https://richie.education/docs/2.8.0/web-analyticsweekly0.5https://richie.education/docs/2.8.1/accessibility-testingweekly0.5https://richie.education/docs/2.8.1/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.8.1/building-the-frontendweekly0.5https://richie.education/docs/2.8.1/contributing-guideweekly0.5https://richie.education/docs/2.8.1/css-guidelinesweekly0.5https://richie.education/docs/2.8.1/discoverweekly0.5https://richie.education/docs/2.8.1/displaying-connection-statusweekly0.5https://richie.education/docs/2.8.1/django-react-interopweekly0.5https://richie.education/docs/2.8.1/docker-developmentweekly0.5https://richie.education/docs/2.8.1/frontend-overridesweekly0.5https://richie.education/docs/2.8.1/internationalizationweekly0.5https://richie.education/docs/2.8.1/lms-backendsweekly0.5https://richie.education/docs/2.8.1/lms-connectionweekly0.5https://richie.education/docs/2.8.1/native-installationweekly0.5https://richie.education/docs/2.8.1/synchronizing-course-runsweekly0.5https://richie.education/docs/2.8.1/tls-connectionweekly0.5https://richie.education/docs/2.8.1/web-analyticsweekly0.5https://richie.education/docs/2.8.2/accessibility-testingweekly0.5https://richie.education/docs/2.8.2/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.8.2/building-the-frontendweekly0.5https://richie.education/docs/2.8.2/contributing-guideweekly0.5https://richie.education/docs/2.8.2/css-guidelinesweekly0.5https://richie.education/docs/2.8.2/discoverweekly0.5https://richie.education/docs/2.8.2/displaying-connection-statusweekly0.5https://richie.education/docs/2.8.2/django-react-interopweekly0.5https://richie.education/docs/2.8.2/docker-developmentweekly0.5https://richie.education/docs/2.8.2/frontend-overridesweekly0.5https://richie.education/docs/2.8.2/internationalizationweekly0.5https://richie.education/docs/2.8.2/lms-backendsweekly0.5https://richie.education/docs/2.8.2/lms-connectionweekly0.5https://richie.education/docs/2.8.2/native-installationweekly0.5https://richie.education/docs/2.8.2/synchronizing-course-runsweekly0.5https://richie.education/docs/2.8.2/tls-connectionweekly0.5https://richie.education/docs/2.8.2/web-analyticsweekly0.5https://richie.education/docs/2.9.0/accessibility-testingweekly0.5https://richie.education/docs/2.9.0/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.9.0/building-the-frontendweekly0.5https://richie.education/docs/2.9.0/contributing-guideweekly0.5https://richie.education/docs/2.9.0/css-guidelinesweekly0.5https://richie.education/docs/2.9.0/discoverweekly0.5https://richie.education/docs/2.9.0/displaying-connection-statusweekly0.5https://richie.education/docs/2.9.0/django-react-interopweekly0.5https://richie.education/docs/2.9.0/docker-developmentweekly0.5https://richie.education/docs/2.9.0/frontend-overridesweekly0.5https://richie.education/docs/2.9.0/internationalizationweekly0.5https://richie.education/docs/2.9.0/lms-backendsweekly0.5https://richie.education/docs/2.9.0/lms-connectionweekly0.5https://richie.education/docs/2.9.0/native-installationweekly0.5https://richie.education/docs/2.9.0/synchronizing-course-runsweekly0.5https://richie.education/docs/2.9.0/tls-connectionweekly0.5https://richie.education/docs/2.9.0/web-analyticsweekly0.5https://richie.education/docs/2.9.1/accessibility-testingweekly0.5https://richie.education/docs/2.9.1/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/2.9.1/building-the-frontendweekly0.5https://richie.education/docs/2.9.1/contributing-guideweekly0.5https://richie.education/docs/2.9.1/css-guidelinesweekly0.5https://richie.education/docs/2.9.1/discoverweekly0.5https://richie.education/docs/2.9.1/displaying-connection-statusweekly0.5https://richie.education/docs/2.9.1/django-react-interopweekly0.5https://richie.education/docs/2.9.1/docker-developmentweekly0.5https://richie.education/docs/2.9.1/frontend-overridesweekly0.5https://richie.education/docs/2.9.1/internationalizationweekly0.5https://richie.education/docs/2.9.1/lms-backendsweekly0.5https://richie.education/docs/2.9.1/lms-connectionweekly0.5https://richie.education/docs/2.9.1/native-installationweekly0.5https://richie.education/docs/2.9.1/synchronizing-course-runsweekly0.5https://richie.education/docs/2.9.1/tls-connectionweekly0.5https://richie.education/docs/2.9.1/web-analyticsweekly0.5https://richie.education/docs/next/accessibility-testingweekly0.5https://richie.education/docs/next/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/next/building-the-frontendweekly0.5https://richie.education/docs/next/contributing-guideweekly0.5https://richie.education/docs/next/cookiecutterweekly0.5https://richie.education/docs/next/css-guidelinesweekly0.5https://richie.education/docs/next/discoverweekly0.5https://richie.education/docs/next/displaying-connection-statusweekly0.5https://richie.education/docs/next/django-react-interopweekly0.5https://richie.education/docs/next/docker-developmentweekly0.5https://richie.education/docs/next/filters-customizationweekly0.5https://richie.education/docs/next/frontend-overridesweekly0.5https://richie.education/docs/next/installationweekly0.5https://richie.education/docs/next/internationalizationweekly0.5https://richie.education/docs/next/joanie-connectionweekly0.5https://richie.education/docs/next/lms-backendsweekly0.5https://richie.education/docs/next/lms-connectionweekly0.5https://richie.education/docs/next/native-installationweekly0.5https://richie.education/docs/next/synchronizing-course-runsweekly0.5https://richie.education/docs/next/tls-connectionweekly0.5https://richie.education/docs/next/web-analyticsweekly0.5https://richie.education/docs/accessibility-testingweekly0.5https://richie.education/docs/api/course-run-synchronization-apiweekly0.5https://richie.education/docs/building-the-frontendweekly0.5https://richie.education/docs/contributing-guideweekly0.5https://richie.education/docs/cookiecutterweekly0.5https://richie.education/docs/css-guidelinesweekly0.5https://richie.education/docs/discoverweekly0.5https://richie.education/docs/displaying-connection-statusweekly0.5https://richie.education/docs/django-react-interopweekly0.5https://richie.education/docs/docker-developmentweekly0.5https://richie.education/docs/filters-customizationweekly0.5https://richie.education/docs/frontend-overridesweekly0.5https://richie.education/docs/installationweekly0.5https://richie.education/docs/internationalizationweekly0.5https://richie.education/docs/joanie-connectionweekly0.5https://richie.education/docs/lms-backendsweekly0.5https://richie.education/docs/lms-connectionweekly0.5https://richie.education/docs/native-installationweekly0.5https://richie.education/docs/synchronizing-course-runsweekly0.5https://richie.education/docs/tls-connectionweekly0.5https://richie.education/docs/web-analyticsweekly0.5https://richie.education/weekly0.5 \ No newline at end of file diff --git a/users/index.html b/users/index.html index 06f5f3024d..bcb712e6a0 100644 --- a/users/index.html +++ b/users/index.html @@ -4,13 +4,13 @@ Richie - - + + - - +

    Who is Using This?

    This project is used by many folks

    France Université NumériqueNAUEDUlib

    Are you using this project?

    Add your company
    + + \ No newline at end of file diff --git a/versions/index.html b/versions/index.html index bf6c8229d6..fa0fe79183 100644 --- a/versions/index.html +++ b/versions/index.html @@ -4,13 +4,13 @@ Richie - - + +
    -

    Richie versions and documentation

    New versions of this project are shipped regularly. Every new version includes its own version of the documentation.

    Versions below 1.12.0 did not have a dedicated documentation website.

    Current version (Stable)

    2.21.1DocumentationRelease Notes

    Pre-release versions

    masterDocumentationSource Code

    Past Versions

    Here you can find previous versions of the documentation.

    2.21.0DocumentationRelease Notes
    2.20.1DocumentationRelease Notes
    2.20.0DocumentationRelease Notes
    2.19.0DocumentationRelease Notes
    2.18.0DocumentationRelease Notes
    2.17.0DocumentationRelease Notes
    2.16.0DocumentationRelease Notes
    2.15.1DocumentationRelease Notes
    2.15.0DocumentationRelease Notes
    2.14.1DocumentationRelease Notes
    2.14.0DocumentationRelease Notes
    2.13.0DocumentationRelease Notes
    2.12.0DocumentationRelease Notes
    2.11.0DocumentationRelease Notes
    2.10.0DocumentationRelease Notes
    2.9.1DocumentationRelease Notes
    2.9.0DocumentationRelease Notes
    2.8.2DocumentationRelease Notes
    2.8.1DocumentationRelease Notes
    2.8.0DocumentationRelease Notes
    2.7.1DocumentationRelease Notes
    2.7.0DocumentationRelease Notes
    2.6.0DocumentationRelease Notes
    2.5.0DocumentationRelease Notes
    2.4.0DocumentationRelease Notes
    2.3.3DocumentationRelease Notes
    2.3.2DocumentationRelease Notes
    2.3.1DocumentationRelease Notes
    2.3.0DocumentationRelease Notes
    2.2.0DocumentationRelease Notes
    2.1.0DocumentationRelease Notes
    2.0.1DocumentationRelease Notes
    2.0.0DocumentationRelease Notes
    1.17DocumentationRelease Notes
    1.16DocumentationRelease Notes
    1.15DocumentationRelease Notes
    1.14DocumentationRelease Notes
    1.13DocumentationRelease Notes
    1.12DocumentationRelease Notes

    You can find past versions of this project on GitHub.

    - - +

    Richie versions and documentation

    New versions of this project are shipped regularly. Every new version includes its own version of the documentation.

    Versions below 1.12.0 did not have a dedicated documentation website.

    Current version (Stable)

    2.22.0DocumentationRelease Notes

    Pre-release versions

    masterDocumentationSource Code

    Past Versions

    Here you can find previous versions of the documentation.

    2.21.1DocumentationRelease Notes
    2.21.0DocumentationRelease Notes
    2.20.1DocumentationRelease Notes
    2.20.0DocumentationRelease Notes
    2.19.0DocumentationRelease Notes
    2.18.0DocumentationRelease Notes
    2.17.0DocumentationRelease Notes
    2.16.0DocumentationRelease Notes
    2.15.1DocumentationRelease Notes
    2.15.0DocumentationRelease Notes
    2.14.1DocumentationRelease Notes
    2.14.0DocumentationRelease Notes
    2.13.0DocumentationRelease Notes
    2.12.0DocumentationRelease Notes
    2.11.0DocumentationRelease Notes
    2.10.0DocumentationRelease Notes
    2.9.1DocumentationRelease Notes
    2.9.0DocumentationRelease Notes
    2.8.2DocumentationRelease Notes
    2.8.1DocumentationRelease Notes
    2.8.0DocumentationRelease Notes
    2.7.1DocumentationRelease Notes
    2.7.0DocumentationRelease Notes
    2.6.0DocumentationRelease Notes
    2.5.0DocumentationRelease Notes
    2.4.0DocumentationRelease Notes
    2.3.3DocumentationRelease Notes
    2.3.2DocumentationRelease Notes
    2.3.1DocumentationRelease Notes
    2.3.0DocumentationRelease Notes
    2.2.0DocumentationRelease Notes
    2.1.0DocumentationRelease Notes
    2.0.1DocumentationRelease Notes
    2.0.0DocumentationRelease Notes
    1.17DocumentationRelease Notes
    1.16DocumentationRelease Notes
    1.15DocumentationRelease Notes
    1.14DocumentationRelease Notes
    1.13DocumentationRelease Notes
    1.12DocumentationRelease Notes

    You can find past versions of this project on GitHub.

    + + \ No newline at end of file