{"id":21029857,"url":"https://github.com/kitconcept/kitconcept.contentcreator","last_synced_at":"2025-05-15T11:32:16.135Z","repository":{"id":38846710,"uuid":"170667619","full_name":"kitconcept/kitconcept.contentcreator","owner":"kitconcept","description":"Create content in a Plone site via JSON files","archived":false,"fork":false,"pushed_at":"2022-09-05T13:49:35.000Z","size":1738,"stargazers_count":2,"open_issues_count":2,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-05-11T12:11:33.337Z","etag":null,"topics":["plone","plone-addon"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kitconcept.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-02-14T09:51:11.000Z","updated_at":"2022-08-02T13:22:54.000Z","dependencies_parsed_at":"2022-09-17T21:55:13.698Z","dependency_job_id":null,"html_url":"https://github.com/kitconcept/kitconcept.contentcreator","commit_stats":null,"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kitconcept%2Fkitconcept.contentcreator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kitconcept%2Fkitconcept.contentcreator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kitconcept%2Fkitconcept.contentcreator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kitconcept%2Fkitconcept.contentcreator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kitconcept","download_url":"https://codeload.github.com/kitconcept/kitconcept.contentcreator/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254330803,"owners_count":22053050,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["plone","plone-addon"],"created_at":"2024-11-19T12:14:42.928Z","updated_at":"2025-05-15T11:32:12.136Z","avatar_url":"https://github.com/kitconcept.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\u003cimg alt=\"logo\" src=\"https://kitconcept.com/logo.svg\" width=\"150\" /\u003e\u003c/div\u003e\n\n\u003ch1 align=\"center\"\u003ekitconcept.contentcreator\u003c/h1\u003e\n\nThis package is the responsible for automated content creation via plone.restapi serializers/creators.\n\nInitially based on `collective.contentcreator` written by Johannes Raggam (@thet) and evolved and improved from it.\n\n\u003cdiv align=\"center\"\u003e\n\n[![PyPI](https://img.shields.io/pypi/v/kitconcept.contentcreator)](https://pypi.org/project/kitconcept.contentcreator/)\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/kitconcept.contentcreator)](https://pypi.org/project/kitconcept.contentcreator/)\n[![PyPI - Wheel](https://img.shields.io/pypi/wheel/kitconcept.contentcreator)](https://pypi.org/project/kitconcept.contentcreator/)\n[![PyPI - License](https://img.shields.io/pypi/l/kitconcept.contentcreator)](https://pypi.org/project/kitconcept.contentcreator/)\n[![PyPI - Status](https://img.shields.io/pypi/status/kitconcept.contentcreator)](https://pypi.org/project/kitconcept.contentcreator/)\n\n\n[![PyPI - Plone Versions](https://img.shields.io/pypi/frameworkversions/plone/kitconcept.contentcreator)](https://pypi.org/project/kitconcept.contentcreator/)\n\n[![Code analysis checks](https://github.com/kitconcept/kitconcept.contentcreator/actions/workflows/code-analysis.yml/badge.svg)](https://github.com/kitconcept/kitconcept.contentcreator/actions/workflows/code-analysis.yml)\n[![Tests](https://github.com/kitconcept/kitconcept.contentcreator/actions/workflows/tests.yml/badge.svg)](https://github.com/kitconcept/kitconcept.contentcreator/actions/workflows/tests.yml)\n![Code Style](https://img.shields.io/badge/Code%20Style-Black-000000)\n\n[![GitHub contributors](https://img.shields.io/github/contributors/kitconcept/kitconcept.contentcreator)](https://github.com/kitconcept/kitconcept.contentcreator)\n[![GitHub Repo stars](https://img.shields.io/github/stars/kitconcept/kitconcept.contentcreator?style=social)](https://github.com/kitconcept/kitconcept.contentcreator)\n\n\u003c/div\u003e\n\n\nUsage\n=====\n\nBasic\n-----\n\nIt allows to have a structure in your policy package like:\n\n```\n|-content_creator\n    |- content.json\n    |- siteroot.json\n    |- de.mysection.json\n    |- ...\n    |- images\n|-content_images\n```\n\nusing these names (for both files and folders) as sensible defaults. This is the\nrecommended way, although you can specify runners for custom JSON files (see below).\n\nand creates the content in a tree like from `content.json` using the runner, and\nobject by object using the standalone json files.\n\nThe {file}`images` folder is blacklisted to support the images folder to be inside the creator folder.\n\nIn your setuphandlers.py you need to:\n\n```python\nfrom kitconcept.contentcreator.creator import content_creator_from_folder\n\ncontent_creator_from_folder()\n```\n\nthe method `content_creator_from_folder` has the following signature:\n\n```python\n    def content_creator_from_folder(\n        folder_name=os.path.join(os.path.dirname(__file__), \"content_creator\"),\n        base_image_path=os.path.join(os.path.dirname(__file__), \"content_images\"),\n        default_lang=None,\n        default_wf_state=None,\n        ignore_wf_types=[\"Image\", \"File\"],\n        logger=logger,\n        temp_enable_content_types=[],\n        custom_order=[],\n        do_not_edit_if_modified_after=None,\n        exclude=[],\n    ):\n\n```\n\nThe creator will bail out (raise) if any object errors on creation (or edition). There are\na couple of environment variables to control this behavior: `CREATOR_DEBUG` and\n`CREATOR_CONTINUE_ON_ERROR`.\n\nOne can exclude elements (ids, without .json sufix) using the `exclude` kwargs.\n\nYou can control if the edit should happen or not for a given element providing the modified date\nof the element is after the one specified in `do_not_edit_if_modified_after` kwargs.\n\nCreator runner given a single file\n----------------------------------\n\nGiven a JSON file containing an array of objects to be created, this runner takes this\narray content (should have plone.restapi syntax compliant structure) and creates content\nout of it. You can load it using the method: `load_json`:\n\n```python\nfrom kitconcept.contentcreator.creator import load_json\n\ncontent_structure = load_json('testcontent/content.json', __file__)\n```\n\nThen you can call the runner with the method `create_item_runner`:\n\n```python\nfrom kitconcept.contentcreator.creator import create_item_runner\n\ncreate_item_runner(\n    api.portal.get(),\n    content_structure,\n    default_lang='en',\n    default_wf_state='published'\n)\n```\n\nCreator runner given a folder with multiple files\n-------------------------------------------------\n\nEach file should contain a single p.restapi JSON compliant object (non arrayed, can't\ncontain other objects). It takes the placement in the tree hierarchy and the object id\nfrom the filename name (eg. de.beispiele.bildergroessen.json)\n\nSetup runners from external modules/packages\n--------------------------------------------\n\nAlternativelly, you can create custom content creators in other packages and\ncall them all at the same time, via a custom adapter:\n\n```python\nfrom kitconcept.contentcreator.interfaces import ICreateTestContent\n\nfor name, provider in getAdapters((api.portal.get(), ), ICreateTestContent):\n    provider()\n```\n\nthis should be the declaration in the other package:\n\n```python\n@implementer(ICreateTestContent)\n@adapter(IPloneSiteRoot)\nclass CreatePFGContent(object):\n    \"\"\"Adapter to create PFG initial content.\"\"\"\n\n    def __init__(self, context):\n        self.context = context\n\n    def __call__(self):\n        content_structure = load_json('testcontent/content.json', __file__)\n\n        create_item_runner(\n            api.portal.get(),\n            content_structure,\n            default_lang='en',\n            default_wf_state='published',\n            ignore_wf_types=[\n                'FormBooleanField',\n                'FormDateField',\n                'FormFileField',\n                'FormFixedPointField',\n                'FormIntegerField',\n                'FormLabelField',\n                'FormLinesField',\n                'FormPasswordField',\n            ],\n        )\n```\n\nother common use is calling from a folder:\n\n```python\nfrom kitconcept.contentcreator.creator import content_creator_from_folder\n\ncontent_creator_from_folder(\n    folder_name=os.path.join(os.path.dirname(__file__), \"content_creator\"),\n    base_image_path=os.path.join(os.path.dirname(__file__), \"images\"),\n    default_lang='en',\n    default_wf_state='published',\n    ignore_wf_types=[\n        'FormBooleanField',\n        'FormDateField',\n        'FormFileField',\n        'FormFixedPointField',\n        'FormIntegerField',\n        'FormLabelField',\n        'FormLinesField',\n        'FormPasswordField',\n    ],\n    logger=logger,\n    temp_enable_content_types=[],\n    custom_order=[\n      'object-id-2.json',\n      'object-id-3.json',\n      'object-id-1.json',\n    ],\n)\n```\n\nImages and Files\n----------------\n\nFor the creation of images, you can use the plone.restapi approach using the\nfollowing serialization mapping containg the file data and some additional\nmetadata:\n\n- **data** - the base64 encoded contents of the file\n- **encoding** - the encoding you used to encode the data, so usually `base64`\n- **content-type** - the MIME type of the file\n- **filename** - the name of the file, including extension\n\n```json\n{\n  \"...\": \"\",\n  \"@type\": \"File\",\n  \"title\": \"My file\",\n  \"file\": {\n    \"data\": \"TG9yZW0gSXBzdW0uCg==\",\n    \"encoding\": \"base64\",\n    \"filename\": \"lorem.txt\",\n    \"content-type\": \"text/plain\"\n  }\n}\n```\n\nAlternatively, you can provide the image an extra property `set_dummy_image`\nwith an array of (image) field names that will create a dummy image placeholder\nin the specified fields in the to be created content type:\n\n```json\n{\n  \"id\": \"an-image\",\n  \"@type\": \"Image\",\n  \"title\": \"Test Image\",\n  \"set_dummy_image\": [\"image\"]\n}\n```\n\nA deprecated syntax form is also supported (it will create the image in the\n`image` field)::\n\n```json\n{\n  \"id\": \"an-image\",\n  \"@type\": \"Image\",\n  \"title\": \"Test Image\",\n  \"set_dummy_image\": true\n}\n```\n\nYou can specify a real image too, using a dict in the `set_local_image` JSON\nattribute with the field name and the filename of the real image:\n\n```json\n{\n  \"id\": \"another-image\",\n  \"@type\": \"Image\",\n  \"title\": \"Another Test Image\",\n  \"set_local_image\": {\"image\": \"image.png\"}\n}\n```\n\nAgain, a deprecated form is also supported (it will create the image in the\n`image` field):\n\n```json\n{\n  \"id\": \"another-image\",\n  \"@type\": \"Image\",\n  \"title\": \"Another Test Image\",\n  \"set_local_image\": \"image.png\"\n}\n```\n\n\nBy default, image scales are generated immediately. To disable this,\nset the `CREATOR_SKIP_SCALES` environment variable.\n\nThe same syntax is valid for files:\n\n```json\n{\n  \"id\": \"an-file\",\n  \"@type\": \"File\",\n  \"title\": \"Test File\",\n  \"set_dummy_file\": [\"file\"]\n}\n```\n\nThe deprecated form is also supported (it will create the file in the\n`file` field):\n\n```json\n{\n  \"id\": \"an-file\",\n  \"@type\": \"File\",\n  \"title\": \"Test File\",\n  \"set_dummy_file\": true\n}\n```\n\nYou can specify a real file too, using a dict in the `set_local_file` JSON\nattribute with the field name and the filename of the real file:\n\n```json\n{\n  \"id\": \"another-file\",\n  \"@type\": \"File\",\n  \"title\": \"Another Test File\",\n  \"set_local_file\": {\"file\": \"file.png\"}\n}\n```\n\nthe deprecated form is also supported (it will create the file in the\n`file` field):\n\n```json\n{\n  \"id\": \"another-file\",\n  \"@type\": \"File\",\n  \"title\": \"Another Test File\",\n  \"set_local_file\": \"file.png\"\n}\n```\n\nFor all local images and files specified, you can specify the `base_path` for the image in the `create_item_runner`:\n\n```python\ncreate_item_runner(\n    api.portal.get(),\n    content_structure,\n    default_lang='en',\n    default_wf_state='published',\n    base_image_path=__file__\n)\n```\n\nTranslations\n------------\n\nIf you are using plone.app.multilingual and creating items from a folder,\nyou can link translations using `translations.csv` in this format::\n\n```csv\ncanonical,translation\n/de/path/to/canonical,/en/path/to/translation\n```\n\nDevelopment\n-----------\n\nRequirements:\n\n- Python 3\n- venv\n\nSetup:\n\n```shell\n  make\n```\n\nRun Static Code Analysis:\n\n```shell\n  make lint\n```\n\nRun Unit / Integration Tests:\n\n```shell\n  make test\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkitconcept%2Fkitconcept.contentcreator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkitconcept%2Fkitconcept.contentcreator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkitconcept%2Fkitconcept.contentcreator/lists"}