diff --git a/.dockerignore b/.dockerignore index 85dcc16..edd0a20 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,2 +1,4 @@ .git node_modules + +democontent/ diff --git a/README.rst b/README.rst index 5aef5fd..8277746 100644 --- a/README.rst +++ b/README.rst @@ -68,6 +68,20 @@ to see sections that can be removed - in each case, the section is noted with a Options are also available for using Postgres/MySQL, uWSGI/Gunicorn/Guvicorn, etc. +Loading with pre-built page on install +====================================== + +It is now possible to load the project with contents which shows how to use +``django-cms`` and ``djangocms-frontend`` to add pages. + +Here is how to use the command: +1. To list the available files in the ``democontent`` folder. + Run ``docker compose run --rm web python manage.py democontent`` + +2. To add a page. Run ``docker compose run --rm web python manage.py democontent `` + +3. To add a page by force. Run ``docker compose run --rm web python manage.py democontent --force`` + Updating requirements ===================== diff --git a/backend/management/__init__.py b/backend/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/management/commands/__init__.py b/backend/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/management/commands/democontent.py b/backend/management/commands/democontent.py new file mode 100644 index 0000000..1765b32 --- /dev/null +++ b/backend/management/commands/democontent.py @@ -0,0 +1,107 @@ +import os +import json + +from django.conf import settings +from django.db import transaction +from django.contrib.auth import get_user_model +from django.core.management.base import BaseCommand + +from cms.api import create_page +from cms.models.contentmodels import PageContent +from cms.constants import TEMPLATE_INHERITANCE_MAGIC + +from djangocms_transfer.importer import import_plugins_to_page +from djangocms_transfer.datastructures import ArchivedPlaceholder, ArchivedPlugin + + +LANGUAGE = "en" +DEMOCONTENT = settings.BASE_DIR / "democontent" +User = get_user_model() + + +class Command(BaseCommand): + help = "Create Demo content." + + def add_arguments(self, parser): + parser.add_argument( + "filepath", metavar="FILE_PATH", nargs="?", help="Input file to load from." + ) + parser.add_argument( + "--force", + "-f", + action="store_true", + dest="force", + help="Force add page to the page tree." + ) + + def _page_create_details(self) -> dict: + user, created = User.objects.get_or_create(username="demo") + if created: + self.stdout.write("User with username, 'demo' created") + + return { + "title": f"{self.title.title()}", + "template": TEMPLATE_INHERITANCE_MAGIC, + "created_by": user, + "in_navigation": True, + "language": LANGUAGE, + } + + def loaddata(self, abs_filepath): + def parse_data(data): + if not data: + return data + if "plugins" in data: + return ArchivedPlaceholder( + slot=data["placeholder"], + plugins=data["plugins"], + ) + if "plugin_type" in data: + return ArchivedPlugin(**data) + return data + + with abs_filepath.open() as fixture: + try: + placeholders: list = json.loads(fixture.read(), object_hook=parse_data) + + # create page and get pagecontent + page = create_page(**self._page_create_details()) + page_content = page.get_admin_content(language=LANGUAGE) + + # import plugin to page + import_plugins_to_page( + placeholders=placeholders, + pagecontent=page_content, + language=LANGUAGE, + ) + except Exception as e: + e.args = (f"Problem installing fixture {fixture}: {e}",) + raise + + def handle(self, *args, **options): + filepath = options["filepath"] + force = options["force"] + + if not filepath: + for file in DEMOCONTENT.iterdir(): + self.stdout.write("---> %s" % file.name) + return + + file = os.path.basename(filepath) + self.title = file.split(".")[0] + + # skip title check if force option is provided + if not force: + all_page_content = PageContent.admin_manager.all() + for page_content in all_page_content: + if page_content.title.lower() == self.title.lower(): + self.stdout.write( + "Page with title('%s') already exists" % self.title + ) + return + + abs_filepath = DEMOCONTENT / file + with transaction.atomic(): + self.loaddata(abs_filepath) + + self.stdout.write("Page with title('%s') created" % self.title) diff --git a/backend/settings.py b/backend/settings.py index 96b289a..c59fc56 100644 --- a/backend/settings.py +++ b/backend/settings.py @@ -50,6 +50,8 @@ 'filer', 'easy_thumbnails', + 'djangocms_transfer', + # the default publishing implementation - optional, but used in most projects 'djangocms_versioning', diff --git a/democontent/gridrowpercolumn.json b/democontent/gridrowpercolumn.json new file mode 100644 index 0000000..37b391b --- /dev/null +++ b/democontent/gridrowpercolumn.json @@ -0,0 +1 @@ +[{"placeholder": "Content", "plugins": [{"pk": 62, "creation_date": "2025-07-15T13:31:14.722Z", "position": 1, "plugin_type": "GridRowPlugin", "parent_id": null, "data": {"ui_item": "GridRow", "tag_type": "div", "config": {"vertical_alignment": "", "horizontal_alignment": "", "gutters": "", "attributes": {}, "row_cols_xs": 3, "row_cols_sm": 7, "row_cols_md": null, "row_cols_lg": null, "row_cols_xl": null, "row_cols_xxl": null, "plugin_title": {"show": false, "title": ""}, "responsive_visibility": null, "margin_x": "", "margin_y": "", "margin_devices": null, "padding_x": "", "padding_y": "", "padding_devices": null}}}, {"pk": 65, "creation_date": "2025-07-17T21:19:38.749Z", "position": 2, "plugin_type": "GridContainerPlugin", "parent_id": null, "data": {"ui_item": "GridContainer", "tag_type": "div", "config": {"size_x": "", "size_y": "", "padding_x": "", "padding_y": "", "padding_devices": null, "margin_x": "", "margin_y": "", "margin_devices": ["xs"], "responsive_visibility": [], "background_context": "", "background_opacity": "", "background_shadow": "", "plugin_title": {"show": false, "title": ""}, "container_type": "container", "attributes": {}}}}]}] diff --git a/democontent/help.json b/democontent/help.json new file mode 100644 index 0000000..b2cad87 --- /dev/null +++ b/democontent/help.json @@ -0,0 +1 @@ +[{"placeholder": "Content", "plugins": [{"pk": 70, "creation_date": "2025-03-13T17:13:26.875Z", "position": 1, "plugin_type": "GridContainerPlugin", "parent_id": null, "data": {"ui_item": "GridContainer", "tag_type": "div", "config": {"container_type": "container", "attributes": {}, "plugin_title": {"show": false, "title": ""}, "background_context": "", "background_opacity": "100", "background_shadow": "", "responsive_visibility": null, "margin_x": "", "margin_y": "", "margin_devices": null, "padding_x": "", "padding_y": "", "padding_devices": null, "size_x": "vw-100", "size_y": "50"}}}, {"pk": 71, "creation_date": "2025-03-13T17:15:04.988Z", "position": 2, "plugin_type": "GridRowPlugin", "parent_id": 70, "data": {"ui_item": "GridRow", "tag_type": "div", "config": {"vertical_alignment": "", "horizontal_alignment": "", "gutters": "", "attributes": {}, "row_cols_xs": null, "row_cols_sm": null, "row_cols_md": null, "row_cols_lg": null, "row_cols_xl": null, "row_cols_xxl": null, "plugin_title": {"show": false, "title": ""}, "responsive_visibility": null, "margin_x": "mx-1", "margin_y": "my-1", "margin_devices": null, "padding_x": "px-1", "padding_y": "py-1", "padding_devices": null}}}, {"pk": 72, "creation_date": "2025-03-13T17:16:12.137Z", "position": 3, "plugin_type": "GridColumnPlugin", "parent_id": 71, "data": {"ui_item": "GridColumn", "tag_type": "div", "config": {"column_alignment": null, "xs_col": null, "xs_order": null, "xs_offset": null, "xs_ml": null, "xs_mr": null, "sm_col": null, "sm_order": null, "sm_offset": null, "sm_ml": null, "sm_mr": null, "md_col": null, "md_order": null, "md_offset": null, "md_ml": null, "md_mr": null, "lg_col": null, "lg_order": null, "lg_offset": null, "lg_ml": null, "lg_mr": null, "xl_col": null, "xl_order": null, "xl_offset": null, "xl_ml": null, "xl_mr": null, "xxl_col": null, "xxl_order": null, "xxl_offset": null, "xxl_ml": null, "xxl_mr": null}}}, {"pk": 73, "creation_date": "2025-03-13T17:17:31.056Z", "position": 4, "plugin_type": "CardPlugin", "parent_id": 72, "data": {"ui_item": "Card", "tag_type": "div", "config": {"card_alignment": "center", "card_outline": "secondary", "card_text_color": "primary", "card_full_height": false, "attributes": {}, "background_context": "light", "background_opacity": "50", "background_shadow": "", "responsive_visibility": null, "margin_x": "", "margin_y": "", "margin_devices": null}}}, {"pk": 74, "creation_date": "2025-03-13T17:17:31.065Z", "position": 5, "plugin_type": "CardInnerPlugin", "parent_id": 73, "data": {"ui_item": "CardInner", "tag_type": "div", "config": {"inner_type": "card-body"}}}, {"pk": 75, "creation_date": "2025-03-13T17:20:28.371Z", "position": 6, "plugin_type": "TextPlugin", "parent_id": 74, "data": {"body": "

Expensive

"}}, {"pk": 76, "creation_date": "2025-03-13T17:16:12.141Z", "position": 7, "plugin_type": "GridColumnPlugin", "parent_id": 71, "data": {"ui_item": "GridColumn", "tag_type": "div", "config": {"column_alignment": null, "xs_col": null, "xs_order": null, "xs_offset": null, "xs_ml": null, "xs_mr": null, "sm_col": null, "sm_order": null, "sm_offset": null, "sm_ml": null, "sm_mr": null, "md_col": null, "md_order": null, "md_offset": null, "md_ml": null, "md_mr": null, "lg_col": null, "lg_order": null, "lg_offset": null, "lg_ml": null, "lg_mr": null, "xl_col": null, "xl_order": null, "xl_offset": null, "xl_ml": null, "xl_mr": null, "xxl_col": null, "xxl_order": null, "xxl_offset": null, "xxl_ml": null, "xxl_mr": null}}}, {"pk": 77, "creation_date": "2025-03-13T17:17:31.056Z", "position": 8, "plugin_type": "CardPlugin", "parent_id": 76, "data": {"ui_item": "Card", "tag_type": "div", "config": {"card_alignment": "", "card_outline": "secondary", "card_text_color": "primary", "card_full_height": false, "attributes": {}, "background_context": "light", "background_opacity": "50", "background_shadow": "", "responsive_visibility": null, "margin_x": "", "margin_y": "", "margin_devices": null}}}, {"pk": 78, "creation_date": "2025-03-13T17:17:31.065Z", "position": 9, "plugin_type": "CardInnerPlugin", "parent_id": 77, "data": {"ui_item": "CardInner", "tag_type": "div", "config": {"inner_type": "card-body"}}}, {"pk": 79, "creation_date": "2025-03-13T17:20:28.371Z", "position": 10, "plugin_type": "TextPlugin", "parent_id": 78, "data": {"body": "

Medium

\n\n

\n\n

\u00a0

"}}, {"pk": 80, "creation_date": "2025-03-14T22:18:23.726Z", "position": 11, "plugin_type": "LinkPlugin", "parent_id": 79, "data": {"template": "default", "name": "Home page", "link": {"internal_link": "cms.page:1", "anchor": "#adebayo"}, "target": "_blank", "attributes": {}}}, {"pk": 81, "creation_date": "2025-03-13T17:15:43.388Z", "position": 12, "plugin_type": "GridColumnPlugin", "parent_id": 71, "data": {"ui_item": "GridColumn", "tag_type": "div", "config": {"column_alignment": null, "xs_col": null, "xs_order": null, "xs_offset": null, "xs_ml": null, "xs_mr": null, "sm_col": null, "sm_order": null, "sm_offset": null, "sm_ml": null, "sm_mr": null, "md_col": null, "md_order": null, "md_offset": null, "md_ml": null, "md_mr": null, "lg_col": null, "lg_order": null, "lg_offset": null, "lg_ml": null, "lg_mr": null, "xl_col": null, "xl_order": null, "xl_offset": null, "xl_ml": null, "xl_mr": null, "xxl_col": null, "xxl_order": null, "xxl_offset": null, "xxl_ml": null, "xxl_mr": null}}}, {"pk": 82, "creation_date": "2025-03-13T17:17:31.056Z", "position": 13, "plugin_type": "CardPlugin", "parent_id": 81, "data": {"ui_item": "Card", "tag_type": "div", "config": {"card_alignment": "", "card_outline": "secondary", "card_text_color": "primary", "card_full_height": false, "attributes": {}, "background_context": "light", "background_opacity": "50", "background_shadow": "", "responsive_visibility": null, "margin_x": "", "margin_y": "", "margin_devices": null}}}, {"pk": 83, "creation_date": "2025-03-13T17:17:31.065Z", "position": 14, "plugin_type": "CardInnerPlugin", "parent_id": 82, "data": {"ui_item": "CardInner", "tag_type": "div", "config": {"inner_type": "card-body"}}}, {"pk": 84, "creation_date": "2025-03-13T17:20:28.371Z", "position": 15, "plugin_type": "TextPlugin", "parent_id": 83, "data": {"body": "

Free

"}}]}] diff --git a/requirements.in b/requirements.in index d8b3bb6..4ddfaab 100644 --- a/requirements.in +++ b/requirements.in @@ -22,6 +22,8 @@ djangocms-admin-style>=3.2.2 # the next-gen text editor djangocms-text +# required to populate demo content +djangocms-transfer>=2.0.0 # optional django CMS frontend djangocms-frontend diff --git a/requirements.txt b/requirements.txt index f45d971..debd968 100644 --- a/requirements.txt +++ b/requirements.txt @@ -52,6 +52,7 @@ django-cms==5.0.1 # djangocms-attributes-field # djangocms-frontend # djangocms-link + # djangocms-transfer # djangocms-versioning django-entangled==0.6.2 # via djangocms-frontend @@ -91,6 +92,8 @@ djangocms-link==5.0.1 # via djangocms-frontend djangocms-text==0.9.1 # via -r requirements.in +djangocms-transfer==2.0.0 + # via -r requirements.in djangocms-versioning==2.3.2 # via -r requirements.in easy-thumbnails[svg]==2.10