From 500663334f164dbef4f1901a753d1d5521a6360c Mon Sep 17 00:00:00 2001 From: Sylvain Boissel Date: Wed, 29 Nov 2023 16:06:59 +0100 Subject: [PATCH 1/8] Add django-extensions for shell_plus --- config/settings.py | 5 +++++ poetry.lock | 16 +++++++++++++++- pyproject.toml | 1 + 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/config/settings.py b/config/settings.py index 7e9d8e16..b6f7bbde 100644 --- a/config/settings.py +++ b/config/settings.py @@ -66,6 +66,11 @@ "content_manager", ] +if DEBUG: + INSTALLED_APPS += [ + "django_extensions", + ] + MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", diff --git a/poetry.lock b/poetry.lock index 2949ca25..223757cf 100644 --- a/poetry.lock +++ b/poetry.lock @@ -404,6 +404,20 @@ django-crispy-forms = ">=2.0,<3.0" django-widget-tweaks = ">=1.4.12,<2.0.0" requests = ">=2.26.0,<3.0.0" +[[package]] +name = "django-extensions" +version = "3.2.3" +description = "Extensions for Django" +optional = false +python-versions = ">=3.6" +files = [ + {file = "django-extensions-3.2.3.tar.gz", hash = "sha256:44d27919d04e23b3f40231c4ab7af4e61ce832ef46d610cc650d53e68328410a"}, + {file = "django_extensions-3.2.3-py3-none-any.whl", hash = "sha256:9600b7562f79a92cbf1fde6403c04fee314608fefbb595502e34383ae8203401"}, +] + +[package.dependencies] +Django = ">=3.2" + [[package]] name = "django-filter" version = "23.3" @@ -1668,4 +1682,4 @@ testing = ["Pillow (>=9.1.0,<11.0.0)", "Wand (>=0.6,<1.0)", "black (==22.3.0)", [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "3d6e6cb1261e6245e15f6ebef5611688839ca1fe77033b06c951c7c75a95e8db" +content-hash = "ffa74afa81f9040df5100e06fc10cd32296d1fbdf5431f73bf2b88dc73f2149a" diff --git a/pyproject.toml b/pyproject.toml index a4413d7e..249a1dd1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,6 +35,7 @@ flake8 = "^6.1.0" isort = "^5.12.0" pre-commit = "^3.5.0" djlint = "^1.34.0" +django-extensions = "^3.2.3" [build-system] requires = ["poetry-core"] From b6c6d3e71abdd07a7460869952a3a98b7a18c4b9 Mon Sep 17 00:00:00 2001 From: Sylvain Boissel Date: Wed, 29 Nov 2023 19:06:10 +0100 Subject: [PATCH 2/8] Create home page --- content_manager/management/__init__.py | 0 .../management/commands/__init__.py | 0 .../commands/create_sample_pages.py | 70 +++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 content_manager/management/__init__.py create mode 100644 content_manager/management/commands/__init__.py create mode 100644 content_manager/management/commands/create_sample_pages.py diff --git a/content_manager/management/__init__.py b/content_manager/management/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/content_manager/management/commands/__init__.py b/content_manager/management/commands/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/content_manager/management/commands/create_sample_pages.py b/content_manager/management/commands/create_sample_pages.py new file mode 100644 index 00000000..dfc5ae1a --- /dev/null +++ b/content_manager/management/commands/create_sample_pages.py @@ -0,0 +1,70 @@ +from django.core.management.base import BaseCommand +from django.urls import reverse +from wagtail.models import Page, Site +from wagtail.rich_text import RichText + +from content_manager.models import ContentPage + + +class Command(BaseCommand): + def add_arguments(self, parser): + parser.add_argument("--slug", nargs="+", type=str, help="[Optional] Slug of the page(s) to create") + + def handle(self, *args, **kwargs): + slugs = kwargs.get("slug") + + if not slugs: + slugs = ["home"] + + for slug in slugs: + self.create_page(slug) + + def create_page(self, slug: str): + if slug == "home": + self.create_page_home() + else: + raise ValueError(f"Valeur inconnue : {slug}") + + def create_page_home(self): + """ + Create the home page, set it as default and delete the sample page + """ + + # Create the page + title = "Votre nouveau site avec le CMS beta" + body = [] + + body.append(("title", {"title": title, "large": True})) + + body_raw = f"""

Bienvenue !

+ +

Vous venez de créer un site utilisant le gestionnaire de contenus de l’État.

+ +

Vous pouvez maintenant vous connecter dans l’administration + et personnaliser le site.

+ +

En particulier, n'hésitez pas à : +

+ """ + + body.append(("paragraph", RichText(body_raw))) + + root = Page.objects.get(slug="root") + home_page = root.add_child(instance=ContentPage(title=title, body=body)) + + # Define it as default + site = Site.objects.first() + + site.root_page_id = home_page.id + site.save() + + # Delete the original default page and get its slug + original_home = Page.objects.get(slug="home") + original_home.delete() + + home_page.slug = "home" + home_page.save() From e8a55525d1a33611036705e5f7e0fbfb664c017b Mon Sep 17 00:00:00 2001 From: Sylvain Boissel Date: Thu, 30 Nov 2023 15:05:02 +0100 Subject: [PATCH 3/8] Add ipython as dev dependency --- poetry.lock | 256 ++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 2 files changed, 256 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 223757cf..17be70a9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -28,6 +28,24 @@ typing-extensions = {version = ">=4", markers = "python_version < \"3.11\""} [package.extras] tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] +[[package]] +name = "asttokens" +version = "2.4.1" +description = "Annotate AST trees with source code positions" +optional = false +python-versions = "*" +files = [ + {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, + {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, +] + +[package.dependencies] +six = ">=1.12.0" + +[package.extras] +astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] +test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] + [[package]] name = "beautifulsoup4" version = "4.11.2" @@ -287,6 +305,17 @@ editorconfig = ">=0.12.2" jsbeautifier = "*" six = ">=1.13.0" +[[package]] +name = "decorator" +version = "5.1.1" +description = "Decorators for Humans" +optional = false +python-versions = ">=3.5" +files = [ + {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, + {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, +] + [[package]] name = "defusedxml" version = "0.7.1" @@ -621,6 +650,34 @@ files = [ {file = "et_xmlfile-1.1.0.tar.gz", hash = "sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c"}, ] +[[package]] +name = "exceptiongroup" +version = "1.2.0" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, + {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "executing" +version = "2.0.1" +description = "Get the currently executing AST node of a frame, and other information" +optional = false +python-versions = ">=3.5" +files = [ + {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"}, + {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"}, +] + +[package.extras] +tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] + [[package]] name = "filelock" version = "3.13.1" @@ -752,6 +809,42 @@ files = [ {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, ] +[[package]] +name = "ipython" +version = "8.18.1" +description = "IPython: Productive Interactive Computing" +optional = false +python-versions = ">=3.9" +files = [ + {file = "ipython-8.18.1-py3-none-any.whl", hash = "sha256:e8267419d72d81955ec1177f8a29aaa90ac80ad647499201119e2f05e99aa397"}, + {file = "ipython-8.18.1.tar.gz", hash = "sha256:ca6f079bb33457c66e233e4580ebfc4128855b4cf6370dddd73842a9563e8a27"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +decorator = "*" +exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} +jedi = ">=0.16" +matplotlib-inline = "*" +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} +prompt-toolkit = ">=3.0.41,<3.1.0" +pygments = ">=2.4.0" +stack-data = "*" +traitlets = ">=5" + +[package.extras] +all = ["black", "curio", "docrepr", "exceptiongroup", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio (<0.22)", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"] +black = ["black"] +doc = ["docrepr", "exceptiongroup", "ipykernel", "matplotlib", "pickleshare", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio (<0.22)", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"] +kernel = ["ipykernel"] +nbconvert = ["nbconvert"] +nbformat = ["nbformat"] +notebook = ["ipywidgets", "notebook"] +parallel = ["ipyparallel"] +qtconsole = ["qtconsole"] +test = ["pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath"] +test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath", "trio"] + [[package]] name = "isort" version = "5.12.0" @@ -769,6 +862,25 @@ pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib" plugins = ["setuptools"] requirements-deprecated-finder = ["pip-api", "pipreqs"] +[[package]] +name = "jedi" +version = "0.19.1" +description = "An autocompletion tool for Python that can be used for text editors." +optional = false +python-versions = ">=3.6" +files = [ + {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"}, + {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"}, +] + +[package.dependencies] +parso = ">=0.8.3,<0.9.0" + +[package.extras] +docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] + [[package]] name = "jmespath" version = "1.0.1" @@ -837,6 +949,20 @@ files = [ {file = "libsass-0.22.0.tar.gz", hash = "sha256:3ab5ad18e47db560f4f0c09e3d28cf3bb1a44711257488ac2adad69f4f7f8425"}, ] +[[package]] +name = "matplotlib-inline" +version = "0.1.6" +description = "Inline Matplotlib backend for Jupyter" +optional = false +python-versions = ">=3.5" +files = [ + {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"}, + {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"}, +] + +[package.dependencies] +traitlets = "*" + [[package]] name = "mccabe" version = "0.7.0" @@ -898,6 +1024,21 @@ files = [ {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, ] +[[package]] +name = "parso" +version = "0.8.3" +description = "A Python Parser" +optional = false +python-versions = ">=3.6" +files = [ + {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, + {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, +] + +[package.extras] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["docopt", "pytest (<6.0.0)"] + [[package]] name = "pathspec" version = "0.11.2" @@ -909,6 +1050,20 @@ files = [ {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, ] +[[package]] +name = "pexpect" +version = "4.9.0" +description = "Pexpect allows easy control of interactive console applications." +optional = false +python-versions = "*" +files = [ + {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, + {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, +] + +[package.dependencies] +ptyprocess = ">=0.5" + [[package]] name = "pillow" version = "10.1.0" @@ -1078,6 +1233,20 @@ nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" +[[package]] +name = "prompt-toolkit" +version = "3.0.41" +description = "Library for building powerful interactive command lines in Python" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "prompt_toolkit-3.0.41-py3-none-any.whl", hash = "sha256:f36fe301fafb7470e86aaf90f036eef600a3210be4decf461a5b1ca8403d3cb2"}, + {file = "prompt_toolkit-3.0.41.tar.gz", hash = "sha256:941367d97fc815548822aa26c2a269fdc4eb21e9ec05fc5d447cf09bad5d75f0"}, +] + +[package.dependencies] +wcwidth = "*" + [[package]] name = "psycopg2-binary" version = "2.9.9" @@ -1159,6 +1328,31 @@ files = [ {file = "psycopg2_binary-2.9.9-cp39-cp39-win_amd64.whl", hash = "sha256:f7ae5d65ccfbebdfa761585228eb4d0df3a8b15cfb53bd953e713e09fbb12957"}, ] +[[package]] +name = "ptyprocess" +version = "0.7.0" +description = "Run a subprocess in a pseudo terminal" +optional = false +python-versions = "*" +files = [ + {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, + {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, +] + +[[package]] +name = "pure-eval" +version = "0.2.2" +description = "Safely evaluate AST nodes without side effects" +optional = false +python-versions = "*" +files = [ + {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, + {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, +] + +[package.extras] +tests = ["pytest"] + [[package]] name = "pycodestyle" version = "2.11.1" @@ -1181,6 +1375,21 @@ files = [ {file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, ] +[[package]] +name = "pygments" +version = "2.17.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, + {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, +] + +[package.extras] +plugins = ["importlib-metadata"] +windows-terminal = ["colorama (>=0.4.6)"] + [[package]] name = "python-dateutil" version = "2.8.2" @@ -1458,6 +1667,25 @@ dev = ["build", "flake8"] doc = ["sphinx"] test = ["pytest", "pytest-cov"] +[[package]] +name = "stack-data" +version = "0.6.3" +description = "Extract data from python stack frames and tracebacks for informative displays" +optional = false +python-versions = "*" +files = [ + {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, + {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, +] + +[package.dependencies] +asttokens = ">=2.1.0" +executing = ">=1.2.0" +pure-eval = "*" + +[package.extras] +tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] + [[package]] name = "static3" version = "0.7.0" @@ -1517,6 +1745,21 @@ notebook = ["ipywidgets (>=6)"] slack = ["slack-sdk"] telegram = ["requests"] +[[package]] +name = "traitlets" +version = "5.14.0" +description = "Traitlets Python configuration system" +optional = false +python-versions = ">=3.8" +files = [ + {file = "traitlets-5.14.0-py3-none-any.whl", hash = "sha256:f14949d23829023013c47df20b4a76ccd1a85effb786dc060f34de7948361b33"}, + {file = "traitlets-5.14.0.tar.gz", hash = "sha256:fcdaa8ac49c04dfa0ed3ee3384ef6dfdb5d6f3741502be247279407679296772"}, +] + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] +test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<7.5)", "pytest-mock", "pytest-mypy-testing"] + [[package]] name = "typing-extensions" version = "4.8.0" @@ -1647,6 +1890,17 @@ development = ["beautifulsoup4 (>=4.8,<4.10)", "coverage (>=4.5)", "django-debug docs = ["Sphinx (>=1.7.4)", "pyenchant (>=2.0)", "sphinx-rtd-theme (>=0.3)", "sphinxcontrib-spelling (>=1.4)"] testing = ["beautifulsoup4 (>=4.8,<4.10)", "coverage (>=4.5)", "django-webtest (>=1.9,<1.10)"] +[[package]] +name = "wcwidth" +version = "0.2.12" +description = "Measures the displayed width of unicode strings in a terminal" +optional = false +python-versions = "*" +files = [ + {file = "wcwidth-0.2.12-py2.py3-none-any.whl", hash = "sha256:f26ec43d96c8cbfed76a5075dac87680124fa84e0855195a6184da9c187f133c"}, + {file = "wcwidth-0.2.12.tar.gz", hash = "sha256:f01c104efdf57971bcb756f054dd58ddec5204dd15fa31d6503ea57947d97c02"}, +] + [[package]] name = "webencodings" version = "0.5.1" @@ -1682,4 +1936,4 @@ testing = ["Pillow (>=9.1.0,<11.0.0)", "Wand (>=0.6,<1.0)", "black (==22.3.0)", [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "ffa74afa81f9040df5100e06fc10cd32296d1fbdf5431f73bf2b88dc73f2149a" +content-hash = "9f76ff0202e291971fb5243813de065dd4a7fe381c0be53e3b26e2dcb046f671" diff --git a/pyproject.toml b/pyproject.toml index 249a1dd1..4bfeae5b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,6 +36,7 @@ isort = "^5.12.0" pre-commit = "^3.5.0" djlint = "^1.34.0" django-extensions = "^3.2.3" +ipython = "^8.18.1" [build-system] requires = ["poetry-core"] From d9e5351f83a34cd5ce0900e9415c75ceb9bce74a Mon Sep 17 00:00:00 2001 From: Sylvain Boissel Date: Thu, 30 Nov 2023 16:04:55 +0100 Subject: [PATCH 4/8] Add a script that creates 3 pages --- Makefile | 1 + .../commands/create_sample_pages.py | 137 +++++++++++++++--- content_manager/utils.py | 35 +++++ 3 files changed, 149 insertions(+), 24 deletions(-) create mode 100644 content_manager/utils.py diff --git a/Makefile b/Makefile index 6780b957..1c87c0af 100644 --- a/Makefile +++ b/Makefile @@ -36,6 +36,7 @@ init: $(EXEC_CMD) poetry run pre-commit install $(EXEC_CMD) poetry run python manage.py migrate $(EXEC_CMD) poetry run python manage.py collectstatic --noinput + $(EXEC_CMD) poetry run python manage.py create_sample_pages .PHONY: runserver runserver: diff --git a/content_manager/management/commands/create_sample_pages.py b/content_manager/management/commands/create_sample_pages.py index dfc5ae1a..f272de00 100644 --- a/content_manager/management/commands/create_sample_pages.py +++ b/content_manager/management/commands/create_sample_pages.py @@ -2,62 +2,123 @@ from django.urls import reverse from wagtail.models import Page, Site from wagtail.rich_text import RichText +from wagtailmenus.models.menuitems import FlatMenuItem from content_manager.models import ContentPage +from content_manager.utils import get_or_create_footer_menu, import_image + + +ALL_ALLOWED_SLUGS = ["home", "mentions-legales", "accessibilite"] class Command(BaseCommand): def add_arguments(self, parser): - parser.add_argument("--slug", nargs="+", type=str, help="[Optional] Slug of the page(s) to create") + parser.add_argument( + "--slug", nargs="+", type=str, help="[Optional] Slug of the page(s) to create", choices=ALL_ALLOWED_SLUGS + ) def handle(self, *args, **kwargs): slugs = kwargs.get("slug") if not slugs: - slugs = ["home"] + slugs = ALL_ALLOWED_SLUGS for slug in slugs: - self.create_page(slug) - - def create_page(self, slug: str): - if slug == "home": - self.create_page_home() - else: - raise ValueError(f"Valeur inconnue : {slug}") - - def create_page_home(self): + if slug == "home": + self.create_homepage() + elif slug == "mentions-legales": + title = "Mentions légales" + body = [] + alert_block = { + "title": title, + "description": """Entrez ici les mentions légales du site.
+ + Que doivent-elles obligatoirement contenir ?""", # noqa + "level": "info", + } + body.append(("alert", alert_block)) + + text_raw = """ +

D’après la documentation du système de design, + le pied de page doit contenir a minima les quatre liens suivants :

+
    +
  • Accessibilité : non/partiellement/totalement conforme
  • +
  • Mentions légales
  • +
  • Données personnelles
  • +
  • Gestion des cookies
  • +
+

Ces deux derniers peuvent pointer vers des pages à part entière ou des sections de cette page.

+ """ # noqa + body.append(("paragraphlarge", RichText(text_raw))) + + self.create_page(slug=slug, title=title, body=body) + elif slug == "accessibilite": + title = "Déclaration d’accessibilité" + body = [] + alert_block = { + "title": title, + "description": """Entrez ici la déclaration d’accessibilité.
+ + Générateur de déclaration d’accessibilité""", + "level": "info", + } + + body.append(("alert", alert_block)) + self.create_page(slug=slug, title=title, body=body, footer_label="Accessibilité : non conforme") + else: + raise ValueError(f"Valeur inconnue : {slug}") + + def create_homepage(self) -> None: """ - Create the home page, set it as default and delete the sample page + Create the homepage, set it as default and delete the sample page """ + # Don't replace a manually created home + already_exists = ContentPage.objects.filter(slug="home").first() + if already_exists: + raise ValueError(f"The home page seem to already exist with id {already_exists.id}") # Create the page - title = "Votre nouveau site avec le CMS beta" body = [] - body.append(("title", {"title": title, "large": True})) + body.append(("title", {"title": "Votre nouveau site avec le CMS Beta", "large": True})) + + image = import_image( + full_path="staticfiles/dsfr/dist/artwork/pictograms/digital/coding.svg", + title="Pictogrammes DSFR — Internet", + ) - body_raw = f"""

Bienvenue !

+ text_raw = """

Bienvenue !

Vous venez de créer un site utilisant le gestionnaire de contenus de l’État.

-

Vous pouvez maintenant vous connecter dans l’administration - et personnaliser le site.

+

Vous pouvez maintenant vous connecter dans l’administration et personnaliser le site.

+ """ + + image_and_text_block = { + "image": image, + "image_ratio": "3", + "text": RichText(text_raw), + "link_url": reverse("wagtailadmin_home"), + "link_label": "Gérer le site", + } + + body.append(("imageandtext", image_and_text_block)) -

En particulier, n'hésitez pas à : + text_2_raw = """ +

En particulier, vous devrez :

    -
  • Remplacer les paramètres par défaut du site dans Configuration > Configuration du site
  • +
  • Configurer le site dans Configuration > Configuration du site
  • Remplacer le contenu de la page de mentions légales
  • Remplacer le contenu de cette page d’accueil.
""" - - body.append(("paragraph", RichText(body_raw))) + body.append(("paragraphlarge", RichText(text_2_raw))) root = Page.objects.get(slug="root") - home_page = root.add_child(instance=ContentPage(title=title, body=body)) + home_page = root.add_child(instance=ContentPage(title="Accueil", body=body, show_in_menus=True)) - # Define it as default - site = Site.objects.first() + # Define it as default for the default site + site = Site.objects.filter(is_default_site=True).first() site.root_page_id = home_page.id site.save() @@ -68,3 +129,31 @@ def create_page_home(self): home_page.slug = "home" home_page.save() + + self.stdout.write(self.style.SUCCESS(f"Homepage created with id {home_page.id}")) + + def create_page(self, slug: str, title: str, body: list, footer_label: str = ""): + """ + Creates a page for the site and adds it to the footer + """ + + # Don't replace a manually created page + already_exists = ContentPage.objects.filter(slug=slug).first() + if already_exists: + raise ValueError(f"The {slug} page seem to already exist with id {already_exists.id}") + + home_page = Page.objects.get(slug="home") + new_page = home_page.add_child(instance=ContentPage(title=title, body=body, slug=slug, show_in_menus=True)) + + footer_menu = get_or_create_footer_menu() + + footer_item = { + "menu": footer_menu, + "link_page": new_page, + } + if footer_label: + footer_item["link_text"] = footer_label + + FlatMenuItem.objects.create(**footer_item) + + self.stdout.write(self.style.SUCCESS(f"Page {slug} created with id {new_page.id}")) diff --git a/content_manager/utils.py b/content_manager/utils.py new file mode 100644 index 00000000..a2439cab --- /dev/null +++ b/content_manager/utils.py @@ -0,0 +1,35 @@ +from io import BytesIO + +from django.core.files.images import ImageFile +from wagtail.images.models import Image +from wagtail.models import Site +from wagtailmenus.models.menus import FlatMenu + + +def import_image(full_path: str, title: str) -> Image: + """ + Import an image to the Wagtail medias based on its full path and return it. + """ + with open(full_path, "rb") as image_file: + image = Image( + file=ImageFile(BytesIO(image_file.read()), name=title), + title=title, + ) + image.save() + return image + + +def get_or_create_footer_menu() -> FlatMenu: + """ + Get the footer menu or create it if it doesn't already exist + + In any case, return it. + """ + + footer_menu = FlatMenu.objects.filter(handle="footer").first() + + if not footer_menu: + default_site = Site.objects.filter(is_default_site=True).first() + footer_menu = FlatMenu.objects.create(title="Pied de page", handle="footer", site=default_site) + + return footer_menu From 6e16557a1b0066eb34d8f5c4ca78c19c5b7223f6 Mon Sep 17 00:00:00 2001 From: Sylvain Boissel Date: Thu, 30 Nov 2023 17:06:11 +0100 Subject: [PATCH 5/8] Change how the home page is contacted --- .../commands/create_sample_pages.py | 4 +-- content_manager/tests/test_utils.py | 33 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 content_manager/tests/test_utils.py diff --git a/content_manager/management/commands/create_sample_pages.py b/content_manager/management/commands/create_sample_pages.py index f272de00..05b1ba05 100644 --- a/content_manager/management/commands/create_sample_pages.py +++ b/content_manager/management/commands/create_sample_pages.py @@ -87,7 +87,7 @@ def create_homepage(self) -> None: title="Pictogrammes DSFR — Internet", ) - text_raw = """

Bienvenue !

+ text_raw = """

Bienvenue !

Vous venez de créer un site utilisant le gestionnaire de contenus de l’État.

@@ -142,7 +142,7 @@ def create_page(self, slug: str, title: str, body: list, footer_label: str = "") if already_exists: raise ValueError(f"The {slug} page seem to already exist with id {already_exists.id}") - home_page = Page.objects.get(slug="home") + home_page = Site.objects.filter(is_default_site=True).first().root_page new_page = home_page.add_child(instance=ContentPage(title=title, body=body, slug=slug, show_in_menus=True)) footer_menu = get_or_create_footer_menu() diff --git a/content_manager/tests/test_utils.py b/content_manager/tests/test_utils.py new file mode 100644 index 00000000..d455c4e6 --- /dev/null +++ b/content_manager/tests/test_utils.py @@ -0,0 +1,33 @@ +from django.contrib.auth.models import User +from wagtail.models import Page +from wagtail.test.utils import WagtailPageTestCase + +from content_manager.models import ContentPage + + +class ContentPageTestCase(WagtailPageTestCase): + def setUp(self): + home = Page.objects.get(slug="home") + self.admin = User.objects.create_superuser("test", "test@test.test", "pass") + self.admin.save() + self.content_page = home.add_child( + instance=ContentPage( + title="Page de contenu", + slug="content-page", + owner=self.admin, + ) + ) + self.content_page.save() + + def test_content_page_is_renderable(self): + self.assertPageIsRenderable(self.content_page) + + def test_content_page_has_minimal_content(self): + url = self.content_page.url + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + + self.assertContains( + response, + "Page de contenu — Titre du site", + ) From c773a4f6c33d796f54ad7f0b6e826cd3377f913e Mon Sep 17 00:00:00 2001 From: Sylvain Boissel Date: Mon, 4 Dec 2023 16:35:41 +0100 Subject: [PATCH 6/8] add tests for utils --- content_manager/tests/test_utils.py | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/content_manager/tests/test_utils.py b/content_manager/tests/test_utils.py index d455c4e6..d40dc40e 100644 --- a/content_manager/tests/test_utils.py +++ b/content_manager/tests/test_utils.py @@ -1,11 +1,14 @@ from django.contrib.auth.models import User +from wagtail.images.models import Image from wagtail.models import Page from wagtail.test.utils import WagtailPageTestCase +from wagtailmenus.models.menus import FlatMenu from content_manager.models import ContentPage +from content_manager.utils import get_or_create_footer_menu, import_image -class ContentPageTestCase(WagtailPageTestCase): +class UtilsTestCase(WagtailPageTestCase): def setUp(self): home = Page.objects.get(slug="home") self.admin = User.objects.create_superuser("test", "test@test.test", "pass") @@ -19,15 +22,17 @@ def setUp(self): ) self.content_page.save() - def test_content_page_is_renderable(self): - self.assertPageIsRenderable(self.content_page) + def test_import_image(self): + image_file = "static/artwork/technical-error.svg" + image = import_image(image_file, "Sample image") - def test_content_page_has_minimal_content(self): - url = self.content_page.url - response = self.client.get(url) - self.assertEqual(response.status_code, 200) + assert isinstance(image, Image) + assert image.title == "Sample image" - self.assertContains( - response, - "Page de contenu — Titre du site", - ) + def test_get_or_create_footer_menu(self): + assert FlatMenu.objects.count() == 0 + + flat_menu = get_or_create_footer_menu() + + assert FlatMenu.objects.count() == 1 + assert flat_menu.handle == "footer" From 987eed15658e9a18bf50c0d4cd67fc4bf88022cc Mon Sep 17 00:00:00 2001 From: Sylvain Boissel Date: Mon, 4 Dec 2023 16:49:00 +0100 Subject: [PATCH 7/8] Remove useless setup method --- content_manager/tests/test_utils.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/content_manager/tests/test_utils.py b/content_manager/tests/test_utils.py index d40dc40e..ff6bbc45 100644 --- a/content_manager/tests/test_utils.py +++ b/content_manager/tests/test_utils.py @@ -1,27 +1,11 @@ -from django.contrib.auth.models import User from wagtail.images.models import Image -from wagtail.models import Page from wagtail.test.utils import WagtailPageTestCase from wagtailmenus.models.menus import FlatMenu -from content_manager.models import ContentPage from content_manager.utils import get_or_create_footer_menu, import_image class UtilsTestCase(WagtailPageTestCase): - def setUp(self): - home = Page.objects.get(slug="home") - self.admin = User.objects.create_superuser("test", "test@test.test", "pass") - self.admin.save() - self.content_page = home.add_child( - instance=ContentPage( - title="Page de contenu", - slug="content-page", - owner=self.admin, - ) - ) - self.content_page.save() - def test_import_image(self): image_file = "static/artwork/technical-error.svg" image = import_image(image_file, "Sample image") From f3869a88b8da8c32fb6ac252fdb36a2df7dcb0da Mon Sep 17 00:00:00 2001 From: Sylvain Boissel Date: Tue, 5 Dec 2023 13:15:52 +0100 Subject: [PATCH 8/8] Add a h1 to all pages --- .../management/commands/create_sample_pages.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/content_manager/management/commands/create_sample_pages.py b/content_manager/management/commands/create_sample_pages.py index 05b1ba05..422f621b 100644 --- a/content_manager/management/commands/create_sample_pages.py +++ b/content_manager/management/commands/create_sample_pages.py @@ -28,7 +28,8 @@ def handle(self, *args, **kwargs): self.create_homepage() elif slug == "mentions-legales": title = "Mentions légales" - body = [] + body = [("title", {"title": title, "large": True})] + alert_block = { "title": title, "description": """Entrez ici les mentions légales du site.
@@ -54,7 +55,8 @@ def handle(self, *args, **kwargs): self.create_page(slug=slug, title=title, body=body) elif slug == "accessibilite": title = "Déclaration d’accessibilité" - body = [] + body = [("title", {"title": title, "large": True})] + alert_block = { "title": title, "description": """Entrez ici la déclaration d’accessibilité.
@@ -78,9 +80,7 @@ def create_homepage(self) -> None: raise ValueError(f"The home page seem to already exist with id {already_exists.id}") # Create the page - body = [] - - body.append(("title", {"title": "Votre nouveau site avec le CMS Beta", "large": True})) + body = [("title", {"title": "Votre nouveau site avec le CMS Beta", "large": True})] image = import_image( full_path="staticfiles/dsfr/dist/artwork/pictograms/digital/coding.svg",