From ae60d103790bde37e0518fbe915f90e4c4d2ae8a Mon Sep 17 00:00:00 2001 From: David Jarvis aka DJ Date: Fri, 2 May 2025 12:57:06 +0100 Subject: [PATCH 01/28] Fri 2 May initial draft --- docs/campaigns/exporting_campaigns.rst | 114 +++++++++++++++++ docs/campaigns/importing_campaigns.rst | 169 +++++++++++++++++++++++++ docs/index.rst | 2 + 3 files changed, 285 insertions(+) create mode 100644 docs/campaigns/exporting_campaigns.rst create mode 100644 docs/campaigns/importing_campaigns.rst diff --git a/docs/campaigns/exporting_campaigns.rst b/docs/campaigns/exporting_campaigns.rst new file mode 100644 index 00000000..151d43a3 --- /dev/null +++ b/docs/campaigns/exporting_campaigns.rst @@ -0,0 +1,114 @@ +.. vale off + +Exporting Campaigns +########################## + +.. vale on + +Before importing or exporting data, as a safety precaution take a backup of your database so that you can restore data if required. Speak to the administrator of your domain to be able to do this as it requires specific technical knowledge. + +.. important:: + The export feature is only available in Mautic 7.0 and later versions. + +Supported Data Types +-------------------- + +Once a campaign is selected, the export feature will extract all campaign data and entities and any dependencies that the campaign needs to function. This includes: + + - Dynamic content + - Assets + - Custom fields + - Other related dependencies + +The export command will: + + - Detect use of plugins and custom fields + - Include the data to support these dependencies + +.. important:: + The importing instance will need the same custom fields and plugins to be present. + +Export Features +*************** + + - Exports data in structured JSON format + - Exports assets into a separate folder in their original format + - Zips the resulting collection of files for easy transfer across systems + - Supports exporting multiple campaigns simultaneously + +Export Methods +************** + +The export feature can be used in three ways: + +**1. UI-Based Export** +---------------------- + +Manual export through Mautic Campaigns dashboard: + +1. Go to the Campaigns menu +2. Select the campaign you want to export +3. Select the export option + +**2. CLI-Based Export** +----------------------- + +Use the following commands: + +.. code-block:: bash + + bin/console mautic:entity:export --entity=campaign --id=1 --zip-file + +* `entity` defines the type of entity to export, in this case `campaign` +* `id` defines the id of the campaign to export +* `zip-file` creates a zip file of the campaign and its dependencies + +.. code-block:: bash + + bin/console mautic:entity:export --entity=campaign --id=1 --json-file + +* Creates only a JSON file and ignores any additional assets and files + +**3. API-Based Export** +----------------------- + +You can export campaigns programmatically using the Mautic API: + +Curl Example +************ + +.. code-block:: bash + + curl --location 'https://{your-mautic-domain}/api/campaigns/export/1' \ + --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \ + --data '' + +Python Example +************** + +.. code-block:: python + + import requests + + # API Endpoint + campaign_id = 1 + url = f'https://{your-mautic-domain}/api/campaigns/export/{campaign_id}' + + # Authentication + headers = { + 'Authorization': 'Bearer YOUR_ACCESS_TOKEN' + } + + # Send export request + response = requests.get(url, headers=headers) + + # Handle response + if response.status_code == 200: + export_file = response.content + # Save or process the exported campaign + +.. important:: + - Replace `{your-mautic-domain}` with your actual Mautic instance domain + - Replace `YOUR_ACCESS_TOKEN` with a valid authentication token + - The API uses a GET request to export a specific campaign by ID + - Ensure you have the necessary API permissions diff --git a/docs/campaigns/importing_campaigns.rst b/docs/campaigns/importing_campaigns.rst new file mode 100644 index 00000000..68972d77 --- /dev/null +++ b/docs/campaigns/importing_campaigns.rst @@ -0,0 +1,169 @@ +.. vale off + +Importing Campaigns +########################## + +.. vale on + +The Import feature allows you to add pre-configured campaigns to your Mautic instance using zip files containing all the relevant data to construct a campaign. + +Import Process +-------------- + +Step-by-Step Import +******************* + +1. **Log into your Mautic account** + Navigate to the main dashboard and authenticate. + +2. **Access Campaigns Section** + Click on the Campaigns menu to view existing campaigns. + +3. **Open Import Options** + - Click the cog icon in the top-right corner + - Select **Import** from the dropdown menu + +4. **Select Campaign File** + On the import screen: + + - Choose the campaign zip file you wish to import + - **Recommended:** Use a ZIP file created from the Mautic export function + - Ensures inclusion of campaign data, external assets, and dynamic content + +.. important:: + Only ZIP files are supported for campaign imports. + +Import Mechanics +**************** + +During the import process, Mautic performs a comprehensive analysis: + +- Checks that the logged in user has the correct permissions to be able to import +- Identifies required entities for campaign functionality +- If a campaign template depends on an external plugin: + + * The import function verifies plugin installation + * When a required plugin is missing: + + - The import process halts + - Prompts the user to install the necessary plugin before continuing + +- Checks for potential ID conflicts in imported entities +- Where conflicts exist, provides options to: + + * Update existing entities + * Create new entities + +- **Automatic Data Mapping** + * Mautic intelligently maps imported data to the correct locations + * Creates any necessary dependent entities automatically + +- **Campaign Activation** + * After successful import, the campaign remains inactive by default, so that you stay in control + * Navigate to the campaign list to activate the imported campaign + + .. tip:: + To activate the imported campaign: + + 1. Go to the Campaigns section + 2. Locate the newly imported campaign + 3. Toggle the campaign status to "Active" + +Importing via the command line +------------------------------ + +You can import campaigns using the Mautic command-line console: + +.. code-block:: bash + + bin/console mautic:entity:import \ + --entity=campaign \ + --file=/tmp/entity_data.zip \ + --user= + +Command Parameters +****************** + +- ``--entity=campaign`` + * Specifies the type of entity being imported + * In this case, importing a campaign + +- ``--file=/tmp/entity_data.zip`` + * Path to the ZIP file containing the campaign data + * Must be a valid export file created from a Mautic export + +- ``--user=`` + * ID of the user performing the import, which is logged against the audit trail + * Ensures proper access and permissions for the import process + +.. important:: + - Ensure the ZIP file is a valid Mautic campaign export + - The specified user must have appropriate import permissions + - Verify the file path is correct before running the command + +Importing using the Mautic API +------------------------------ + +You can import campaigns programmatically using the Mautic API: + +Curl Example (ZIP File) +*********************** + +.. code-block:: bash + + curl -X POST 'https://{your-mautic-domain}/api/campaigns/import' \ + -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \ + -H 'Content-Type: multipart/form-data' \ + -F 'file=@/path/to/campaign_export.zip' + +Python Example (JSON Data) +************************** + +.. code-block:: python + + import requests + + # API Endpoint + url = 'https://{your-mautic-domain}/api/campaigns/import' + + # Authentication + headers = { + 'Authorization': 'Bearer YOUR_ACCESS_TOKEN', + 'Content-Type': 'application/json' + } + + # Campaign import data + payload = { + 'name': 'Imported Campaign', + 'description': 'Campaign imported via API', + # Add other campaign details as needed + } + + # Send import request + response = requests.post(url, headers=headers, json=payload) + + # Handle response + if response.status_code == 200: + imported_campaign = response.json() + print("Campaign imported successfully") + +API Import Methods +****************** + +Mautic supports two primary methods of API-based campaign import: + +1. **ZIP File Import** + - Use ``multipart/form-data`` content type + - Upload the complete campaign export ZIP file + - Includes all campaign assets and dependencies + +2. **JSON Data Import** + - Use ``application/json`` content type + - Send campaign details directly in the request body + - Useful for creating new campaigns or updating existing ones + +.. important:: + - Replace ``{your-mautic-domain}`` with your actual Mautic instance domain + - Ensure you have a valid access token by accessing the API Credentials section within Mautic settings and either creating new credentials or retrieving existing ones from the list of authorized API clients. + - The imported campaign must comply with Mautic's campaign structure + - Verify import permissions and data integrity diff --git a/docs/index.rst b/docs/index.rst index f9a9989d..2f6c3d77 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -94,6 +94,8 @@ There are different types of documentation available to help you navigate your w campaigns/creating_campaigns campaigns/campaign_builder campaigns/managing_campaigns + campaigns/exporting_campaigns + campaigns/importing_campaigns campaigns/troubleshooting_campaigns .. toctree:: From 3f908d9fbd9716bdb487ab6e68503a8d5f226d4c Mon Sep 17 00:00:00 2001 From: David Jarvis aka DJ Date: Thu, 22 May 2025 12:29:56 +0100 Subject: [PATCH 02/28] Add .DS_Store to gitignore --- .gitignore | 157 +---------------------------------------------------- 1 file changed, 2 insertions(+), 155 deletions(-) diff --git a/.gitignore b/.gitignore index a8c18321..4f074fd0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,155 +1,2 @@ - -# Created by https://www.toptal.com/developers/gitignore/api/python,jupyternotebooks -# Edit at https://www.toptal.com/developers/gitignore?templates=python,jupyternotebooks - -### JupyterNotebooks ### -# gitignore template for Jupyter Notebooks -# website: http://jupyter.org/ - -.ipynb_checkpoints -*/.ipynb_checkpoints/* - -# IPython -profile_default/ -ipython_config.py - -# Remove previous ipynb_checkpoints -# git rm -r .ipynb_checkpoints/ - -### Python ### -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook - -# IPython - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# End of https://www.toptal.com/developers/gitignore/api/python,jupyternotebooks +.DS_Store +**/.DS_Store From c6d5bd7d58b80d6c9150f4cf54f58b282b6bac23 Mon Sep 17 00:00:00 2001 From: David Jarvis aka DJ Date: Wed, 7 May 2025 13:20:53 +0100 Subject: [PATCH 03/28] Updated campaign import/export documentation --- .DS_Store | Bin 0 -> 6148 bytes docs/.DS_Store | Bin 0 -> 6148 bytes docs/campaigns/.DS_Store | Bin 0 -> 6148 bytes docs/campaigns/exporting_campaigns.rst | 17 +++++++----- docs/campaigns/images/activate-campaign.png | Bin 0 -> 40716 bytes docs/campaigns/importing_campaigns.rst | 27 +++++++++++--------- 6 files changed, 25 insertions(+), 19 deletions(-) create mode 100644 .DS_Store create mode 100644 docs/.DS_Store create mode 100644 docs/campaigns/.DS_Store create mode 100644 docs/campaigns/images/activate-campaign.png diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..9c5665fae40a518ff995e97b94abb301051d93eb GIT binary patch literal 6148 zcmeHK%}T>S5Z-O8Z74zy3LZQxc&*q_iY;D3tS?|h4=Ob=MT0S0n$#RhAy1$$7(XTQMU%>bb0u2_OLzt4v!-Wnn#3}>hp{rgj3Xol zhyh}N7+6mR%z4mgu4nmFH8DU8{J;S24+0vZW3bSuwhrj<`i%Y>A`0mEmOvB+9fO5N z@PKfg3aC@Ld17##4&%baIR*=jI-PN~GR$LEE*~#ktq$Wtg){DGq@EZc29_D9YO{ss z|0(=3DJaA`EHvUQIIhwG=^~&Ap^g~%1qMCaPN85GN`-spNhHqL9Z%x6JG!OS zl@OsfpS#|5&`Y}Iy>k(b-5~0XWI*8e(B#U%1$8SAJG%PLyk^5Jw^Yc(4c ze%xp@W)(iIowk}4UaQwzvzb-gKRP+P>^_HsNW3aC8J;>RyBg>4hLeS?UYt=Bil~oP zim>1jTo}L*#{l{eLA-Eo|9akQvip|*oF#*X0mH!dGN5nIVrhFRFqs<$3ZYfKfQ1;zXbNE*yw82C{JJ^?$&jIsa# literal 0 HcmV?d00001 diff --git a/docs/campaigns/.DS_Store b/docs/campaigns/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..a0560a6cbe79e5d2d6b6b5aa977abb7764d93322 GIT binary patch literal 6148 zcmeHK%}T>S5Z-NTlTd^l6vWel*Mcpz)Z!)7`T|Dupi&cBYB0@~CbfrB$OGsL`5-=z zGrOBZu;5L^&cN$6^`DZ1V6 zrYY9#UjMu)+MP~+vvKUhlhcdq(Mvo_f04v8nS#B@DPjqU z0b+m{AO?1g0do@Aja@68>LmtK+e)#_juDx7goBlW}pF;Hfps)sI~ z|5xzKY<=XHQ)omC5Ci{=0bU#W!vPd!&em_`;aMx7JwZdkxEvJ_&`Xy97~no~q@3C> bP=`3rV6G8oLAy!^q>F$eggRp27Z~^g-Iz>_ literal 0 HcmV?d00001 diff --git a/docs/campaigns/exporting_campaigns.rst b/docs/campaigns/exporting_campaigns.rst index 151d43a3..99eec1ec 100644 --- a/docs/campaigns/exporting_campaigns.rst +++ b/docs/campaigns/exporting_campaigns.rst @@ -8,7 +8,7 @@ Exporting Campaigns Before importing or exporting data, as a safety precaution take a backup of your database so that you can restore data if required. Speak to the administrator of your domain to be able to do this as it requires specific technical knowledge. .. important:: - The export feature is only available in Mautic 7.0 and later versions. + Both the import and the export features are only available in Mautic 7.0 and later versions. Supported Data Types -------------------- @@ -28,13 +28,16 @@ The export command will: .. important:: The importing instance will need the same custom fields and plugins to be present. -Export Features +Export Mechanic *************** - - Exports data in structured JSON format +Whether exporting via the UI, the command line or using the API, the export feature +follows the same process. + - Checks that the user has adequate permissions to export + - Supports exporting multiple campaigns simultaneously + - Exports data in a structured JSON format - Exports assets into a separate folder in their original format - Zips the resulting collection of files for easy transfer across systems - - Supports exporting multiple campaigns simultaneously Export Methods ************** @@ -48,7 +51,7 @@ Manual export through Mautic Campaigns dashboard: 1. Go to the Campaigns menu 2. Select the campaign you want to export -3. Select the export option +3. Select the export option from the dropdown menu located next to the item selection **2. CLI-Based Export** ----------------------- @@ -60,7 +63,7 @@ Use the following commands: bin/console mautic:entity:export --entity=campaign --id=1 --zip-file * `entity` defines the type of entity to export, in this case `campaign` -* `id` defines the id of the campaign to export +* `id` defines the id of the campaign to export. Look at the URL to find the ID when you view or edit the campaign - the ID will appear in the URL e.g., /s/campaigns/view/123 where 123 is the ID * `zip-file` creates a zip file of the campaign and its dependencies .. code-block:: bash @@ -72,7 +75,7 @@ Use the following commands: **3. API-Based Export** ----------------------- -You can export campaigns programmatically using the Mautic API: +You can export campaigns programmatically using the Mautic API. You will need to authenticate for the API request. using the API credentials stored in Mautic's settings. For more detail on how to authenticate, see the `Mautic API documentation `_. Curl Example ************ diff --git a/docs/campaigns/images/activate-campaign.png b/docs/campaigns/images/activate-campaign.png new file mode 100644 index 0000000000000000000000000000000000000000..064b5de2f31cb7b29bb7c922146c1f4228c5da5d GIT binary patch literal 40716 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 literal 0 HcmV?d00001 diff --git a/docs/campaigns/importing_campaigns.rst b/docs/campaigns/importing_campaigns.rst index 68972d77..d6f7ddba 100644 --- a/docs/campaigns/importing_campaigns.rst +++ b/docs/campaigns/importing_campaigns.rst @@ -31,16 +31,16 @@ Step-by-Step Import - Ensures inclusion of campaign data, external assets, and dynamic content .. important:: - Only ZIP files are supported for campaign imports. + Only ZIP files are supported for campaign imports using the UI. In addition, correctly structured JSON files can be used with both the command line and API endpoints. Import Mechanics **************** -During the import process, Mautic performs a comprehensive analysis: +During the import process, Mautic performs a comprehensive analysis of the data to be imported: - Checks that the logged in user has the correct permissions to be able to import - Identifies required entities for campaign functionality -- If a campaign template depends on an external plugin: +- Validates if a campaign template depends on an external plugin: * The import function verifies plugin installation * When a required plugin is missing: @@ -48,11 +48,11 @@ During the import process, Mautic performs a comprehensive analysis: - The import process halts - Prompts the user to install the necessary plugin before continuing -- Checks for potential ID conflicts in imported entities +- Validates for potential ID conflicts in imported entities - Where conflicts exist, provides options to: - * Update existing entities - * Create new entities + * Update existing entities (allowing administrators to update existing campaigns) + * Create new entities, using a new ID - **Automatic Data Mapping** * Mautic intelligently maps imported data to the correct locations @@ -60,14 +60,17 @@ During the import process, Mautic performs a comprehensive analysis: - **Campaign Activation** * After successful import, the campaign remains inactive by default, so that you stay in control - * Navigate to the campaign list to activate the imported campaign - + * Navigate to the campaign list to activate imported campaigns .. tip:: - To activate the imported campaign: +To activate the imported campaign: 1. Go to the Campaigns section - 2. Locate the newly imported campaign + 2. Locate the newly imported campaign using the toggle next to the campaign name |activate_toggle|. 3. Toggle the campaign status to "Active" + + .. |activate_toggle| image:: images/activate-campaign.png + :alt: Activate campaign toggle + :height: 20px Importing via the command line ------------------------------ @@ -155,7 +158,7 @@ Mautic supports two primary methods of API-based campaign import: 1. **ZIP File Import** - Use ``multipart/form-data`` content type - Upload the complete campaign export ZIP file - - Includes all campaign assets and dependencies + - Includes all campaign assets and dependencies from the ZIP file 2. **JSON Data Import** - Use ``application/json`` content type @@ -164,6 +167,6 @@ Mautic supports two primary methods of API-based campaign import: .. important:: - Replace ``{your-mautic-domain}`` with your actual Mautic instance domain - - Ensure you have a valid access token by accessing the API Credentials section within Mautic settings and either creating new credentials or retrieving existing ones from the list of authorized API clients. + - Ensure you have a valid access token by accessing the API Credentials section within Mautic's settings. - The imported campaign must comply with Mautic's campaign structure - Verify import permissions and data integrity From e57b5e2ff19bea7371b0239625b6699ae98d4a9d Mon Sep 17 00:00:00 2001 From: David Jarvis aka DJ Date: Fri, 9 May 2025 11:39:51 +0100 Subject: [PATCH 04/28] updates to user docs for campaign export and import --- docs/campaigns/images/campaign-sample.json | 573 +++++++++++++++++++++ 1 file changed, 573 insertions(+) create mode 100644 docs/campaigns/images/campaign-sample.json diff --git a/docs/campaigns/images/campaign-sample.json b/docs/campaigns/images/campaign-sample.json new file mode 100644 index 00000000..e8a54b30 --- /dev/null +++ b/docs/campaigns/images/campaign-sample.json @@ -0,0 +1,573 @@ +[ + { + "campaign": [ + { + "id": 3, + "name": "Sample campaign file", + "description": "", + "is_published": false, + "canvas_settings": { + "nodes": [ + { + "id": 5, + "positionX": "379", + "positionY": "159" + }, + { + "id": 7, + "positionX": "772", + "positionY": "155" + }, + { + "id": 8, + "positionX": "73", + "positionY": "158" + }, + { + "id": 9, + "positionX": "506", + "positionY": "408" + }, + { + "id": 6, + "positionX": "213", + "positionY": "263" + }, + { + "id": "lists", + "positionX": "653", + "positionY": "53" + }, + { + "id": "forms", + "positionX": "947", + "positionY": "51" + } + ], + "connections": [ + { + "sourceId": "lists", + "targetId": 5, + "anchors": { + "source": "leadsource", + "target": "top" + } + }, + { + "sourceId": 5, + "targetId": 6, + "anchors": { + "source": "yes", + "target": "top" + } + }, + { + "sourceId": "lists", + "targetId": 7, + "anchors": { + "source": "leadsource", + "target": "top" + } + }, + { + "sourceId": "lists", + "targetId": 8, + "anchors": { + "source": "leadsource", + "target": "top" + } + }, + { + "sourceId": "lists", + "targetId": 9, + "anchors": { + "source": "leadsource", + "target": "top" + } + }, + { + "sourceId": "lists", + "targetId": "forms", + "anchors": { + "source": "leadsourceright", + "target": "leadsourceleft" + } + } + ] + }, + "uuid": "b4ddc4d7-149e-4a81-9141-0e03c598627a" + } + ], + "campaign_event": [ + { + "id": 5, + "campaign_id": 3, + "name": "Downloads asset", + "description": "", + "type": "asset.download", + "event_type": "decision", + "event_order": 1, + "properties": { + "canvasSettings": { + "droppedX": "313", + "droppedY": "158" + }, + "name": "", + "anchor": "leadsource", + "properties": { + "assets": [ + 2 + ] + }, + "type": "asset.download", + "eventType": "decision", + "anchorEventType": "source", + "campaignId": "3", + "_token": "0.oKNciNUEGsLHNnAw81-PkskwW94qK_8ClWqMXITDlz4.k5ZuwZo1VY6kZz5jvS-4-bNUD4h5ToVr3wHOa-OHp2mQ0xHfozNsiPIAGw", + "buttons": { + "save": "" + }, + "triggerDate": null, + "assets": [ + 2 + ] + }, + "trigger_interval": 0, + "trigger_interval_unit": "", + "trigger_mode": "", + "triggerDate": null, + "channel": "asset", + "channel_id": 2, + "parent_id": null, + "uuid": "2ea825c4-6086-4ec8-b355-44f7355d4bc0" + }, + { + "id": 7, + "campaign_id": 3, + "name": "Request dynamic content", + "description": "", + "type": "dwc.decision", + "event_type": "decision", + "event_order": 1, + "properties": { + "canvasSettings": { + "droppedX": "553", + "droppedY": "158" + }, + "name": "", + "anchor": "leadsource", + "properties": { + "dwc_slot_name": "test", + "dynamicContent": 1 + }, + "type": "dwc.decision", + "eventType": "decision", + "anchorEventType": "source", + "campaignId": "3", + "_token": "04feb2c5a9.SYXSFiT87s-Zw_AIqYLsKEyHuLu3w2dVy9oma5rraFw.GsjjfWq5r6LBgrl-2NO-ay7_7PmOkh8ls7QeIN2xKR0Yt4g7c5KPqfyklw", + "buttons": { + "save": "" + }, + "triggerDate": null, + "dwc_slot_name": "test", + "dynamicContent": 1 + }, + "trigger_interval": 0, + "trigger_interval_unit": "", + "trigger_mode": "", + "triggerDate": null, + "channel": "dynamicContent", + "channel_id": 1, + "parent_id": null, + "uuid": "21ec254c-68d5-4675-aae0-4d698676a9ce" + }, + { + "id": 8, + "campaign_id": 3, + "name": "Visits a page", + "description": "", + "type": "page.pagehit", + "event_type": "decision", + "event_order": 1, + "properties": { + "canvasSettings": { + "droppedX": "73", + "droppedY": "158" + }, + "name": "", + "anchor": "leadsource", + "properties": { + "pages": [ + 1 + ], + "url": "", + "referer": "" + }, + "type": "page.pagehit", + "eventType": "decision", + "anchorEventType": "source", + "campaignId": "3", + "_token": "5f8ffc233728216b01c925d3b1.-S7eUs8NTLUMFt8d0pR5J8yCaeCdnLbL4RAthmMKL8U.qmPvOYFIDdhUV5Zro8UrZK76PaKkzc67mX4VzSRQboSoHIR_mGMt02lxuA", + "buttons": { + "save": "" + }, + "triggerDate": null, + "pages": [ + 1 + ], + "url": null, + "referer": null + }, + "trigger_interval": 0, + "trigger_interval_unit": "", + "trigger_mode": "", + "triggerDate": null, + "channel": "page", + "channel_id": 1, + "parent_id": null, + "uuid": "2a29709f-0cf7-435e-afab-aff130965789" + }, + { + "id": 9, + "campaign_id": 3, + "name": "test", + "description": "", + "type": "lead.points", + "event_type": "condition", + "event_order": 1, + "properties": { + "canvasSettings": { + "droppedX": "10", + "droppedY": "263" + }, + "name": "test", + "triggerMode": "immediate", + "triggerDate": null, + "triggerInterval": "1", + "triggerIntervalUnit": "d", + "triggerHour": "", + "triggerRestrictedStartHour": "", + "triggerRestrictedStopHour": "", + "triggerWindow": "0", + "anchor": "leadsource", + "properties": { + "operator": "=", + "score": "1111", + "group": "1" + }, + "type": "lead.points", + "eventType": "condition", + "anchorEventType": "source", + "campaignId": "3", + "_token": "91d612e76a6bf.bDrd_GtYJSCQAa8oNqzeGhljlPlKZ9gy66WBo3Qer08.P3fslyUdZE3IQOZeR_2MWXsbwLtzNqBCk8u56DNE7g49CIfRPDZERvVmyA", + "buttons": { + "save": "" + }, + "operator": "=", + "score": 1111, + "group": 1 + }, + "trigger_interval": 1, + "trigger_interval_unit": "d", + "trigger_mode": "immediate", + "triggerDate": null, + "channel": "", + "channel_id": 0, + "parent_id": null, + "uuid": "bc70df3e-7d49-4d12-b6ad-992f1e3205a4" + }, + { + "id": 6, + "campaign_id": 3, + "name": "test", + "description": "", + "type": "email.send", + "event_type": "action", + "event_order": 2, + "properties": { + "canvasSettings": { + "droppedX": "213", + "droppedY": "263" + }, + "name": "test", + "triggerMode": "immediate", + "triggerDate": null, + "triggerInterval": "1", + "triggerIntervalUnit": "d", + "triggerHour": "", + "triggerRestrictedStartHour": "", + "triggerRestrictedStopHour": "", + "triggerWindow": "0", + "anchor": "yes", + "properties": { + "email": 1, + "email_type": "marketing", + "priority": "2", + "attempts": "3" + }, + "type": "email.send", + "eventType": "action", + "anchorEventType": "decision", + "campaignId": "3", + "_token": "fa386d8.ZuglYE5nMxIig26ZT_ooaIBQV5jFhTurjLKYxG0W_dM.NaUUCwAicn96wifvPqt6K-IoA9r81EPb9NygjypMvJI32n9NGQlSdEfkCQ", + "buttons": { + "save": "" + }, + "email": 1, + "email_type": "marketing", + "priority": 2, + "attempts": 3 + }, + "trigger_interval": 1, + "trigger_interval_unit": "d", + "trigger_mode": "immediate", + "triggerDate": null, + "channel": "email", + "channel_id": 1, + "parent_id": 5, + "uuid": "63077b6b-800b-4ed1-88ca-3267e3012947" + } + ], + "asset": [ + { + "id": 2, + "is_published": true, + "title": "stesetste", + "description": null, + "alias": "stesetste", + "storage_location": "local", + "path": "e2ac32c7d50379f5df60cc43d1e7181fa5e94ab9.png", + "remote_path": null, + "original_file_name": "icon_128x128.png", + "lang": "en", + "publish_up": null, + "publish_down": null, + "extension": "png", + "mime": "image\/png", + "size": 10, + "disallow": true, + "uuid": "21a5c18a-3c34-4871-92c1-b698eb5ac80f" + } + ], + "dynamicContent": [ + { + "id": 1, + "translation_parent_id": null, + "variant_parent_id": null, + "is_published": true, + "name": "test", + "description": null, + "publish_up": null, + "publish_down": null, + "content": null, + "utm_tags": { + "utmSource": null, + "utmMedium": null, + "utmCampaign": null, + "utmContent": null + }, + "lang": "en", + "variant_settings": [], + "variant_start_date": null, + "filters": [], + "is_campaign_based": true, + "slot_name": "", + "uuid": "107ac8a2-ec01-4ea9-88e8-80d24e2a63bc" + } + ], + "page": [ + { + "id": 1, + "is_published": true, + "title": "test page", + "alias": "test-page", + "template": "blank", + "custom_html": "\r\n \r\n {pagetitle}<\/title>\r\n <meta name=\"description\" content=\"{pagemetadescription}\" \/>\r\n \r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n <\/head><body><div data-section-wrapper=\"1\"><center><table data-section=\"1\" width=\"600\" cellpadding=\"0\" cellspacing=\"0\" id=\"iuuk\"><tbody><tr><td><div data-slot-container=\"1\" id=\"i70n\"><div data-slot=\"text\"><br \/><h2>Hello there!<\/h2><br \/>\r\n We haven't heard from you for a while...\r\n <br \/><br \/><br \/><\/div><\/div><\/td><\/tr><\/tbody><\/table><\/center><\/div><style>#iuuk{width:600;}#i70n{min-height:30px;}<\/style><\/body><\/html>", + "content": [], + "publish_up": null, + "publish_down": null, + "hits": 0, + "unique_hits": 0, + "variant_hits": 0, + "revision": 6, + "meta_description": null, + "head_script": null, + "footer_script": null, + "redirect_type": null, + "redirect_url": null, + "is_preference_center": false, + "no_index": false, + "lang": "en", + "variant_settings": [], + "uuid": "401fbafc-707e-44d8-87c2-3f823f17546b" + } + ], + "pointGroup": [ + { + "id": 1, + "name": "test", + "description": null, + "is_published": true, + "uuid": "895c3582-99d7-4174-bb2b-e33a38324882" + } + ], + "email": [ + { + "id": 1, + "translation_parent_id": null, + "variant_parent_id": null, + "unsubscribeform_id": null, + "preference_center_id": null, + "is_published": true, + "name": "test email 1", + "description": null, + "subject": "test", + "preheader_text": null, + "from_name": null, + "use_owner_as_mailer": false, + "template": "mautic_code_mode", + "content": [], + "utm_tags": { + "utmSource": null, + "utmMedium": null, + "utmCampaign": null, + "utmContent": null + }, + "plain_text": null, + "custom_html": "<!doctype html>\r\n<html lang=\"und\" dir=\"auto\" xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\" xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:o=\"urn:schemas-microsoft-com:office:office\">\r\n\r\n<head>\r\n <title><\/title>\r\n <!--[if !mso]><!-->\r\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\r\n <!--<![endif]-->\r\n <meta http-equiv=\"Content-Type\" content=\"text\/html; charset=UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\r\n <style type=\"text\/css\">\r\n #outlook a {\r\n padding: 0;\r\n }\r\n\r\n body {\r\n margin: 0;\r\n padding: 0;\r\n -webkit-text-size-adjust: 100%;\r\n -ms-text-size-adjust: 100%;\r\n }\r\n\r\n table,\r\n td {\r\n border-collapse: collapse;\r\n mso-table-lspace: 0pt;\r\n mso-table-rspace: 0pt;\r\n }\r\n\r\n img {\r\n border: 0;\r\n height: auto;\r\n line-height: 100%;\r\n outline: none;\r\n text-decoration: none;\r\n -ms-interpolation-mode: bicubic;\r\n }\r\n\r\n p {\r\n display: block;\r\n margin: 13px 0;\r\n }\r\n <\/style>\r\n <!--[if mso]>\r\n <noscript>\r\n <xml>\r\n <o:OfficeDocumentSettings>\r\n <o:AllowPNG\/>\r\n <o:PixelsPerInch>96<\/o:PixelsPerInch>\r\n <\/o:OfficeDocumentSettings>\r\n <\/xml>\r\n <\/noscript>\r\n <![endif]-->\r\n <!--[if lte mso 11]>\r\n <style type=\"text\/css\">\r\n .mj-outlook-group-fix { width:100% !important; }\r\n <\/style>\r\n <![endif]-->\r\n <!--[if !mso]><!-->\r\n <link href=\"https:\/\/fonts.googleapis.com\/css?family=Open+Sans:300,400,500,700\" rel=\"stylesheet\" type=\"text\/css\">\r\n <link href=\"https:\/\/fonts.googleapis.com\/css?family=Ubuntu:300,400,500,700\" rel=\"stylesheet\" type=\"text\/css\">\r\n <style type=\"text\/css\">\r\n @import url(https:\/\/fonts.googleapis.com\/css?family=Open+Sans:300,400,500,700);\r\n @import url(https:\/\/fonts.googleapis.com\/css?family=Ubuntu:300,400,500,700);\r\n <\/style>\r\n <!--<![endif]-->\r\n <style type=\"text\/css\">\r\n @media only screen and (min-width:480px) {\r\n .mj-column-px-550 {\r\n width: 550px !important;\r\n max-width: 550px;\r\n }\r\n }\r\n <\/style>\r\n <style media=\"screen and (min-width:480px)\">\r\n .moz-text-html .mj-column-px-550 {\r\n width: 550px !important;\r\n max-width: 550px;\r\n }\r\n <\/style>\r\n <!-- CSS-STYLE -->\r\n<\/head>\r\n\r\n<body style=\"word-spacing:normal;\">\r\n <div style lang=\"und\" dir=\"auto\">\r\n <!--[if mso | IE]><table align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" class=\"\" role=\"presentation\" style=\"width:600px;\" width=\"600\" bgcolor=\"#ffffff\" ><tr><td style=\"line-height:0px;font-size:0px;mso-line-height-rule:exactly;\"><![endif]-->\r\n <div style=\"background:#ffffff;background-color:#ffffff;margin:0px auto;max-width:600px;\">\r\n <table align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"background:#ffffff;background-color:#ffffff;width:100%;\">\r\n <tbody>\r\n <tr>\r\n <td style=\"direction:ltr;font-size:0px;padding:20px 0;text-align:center;\">\r\n <!--[if mso | IE]><table role=\"presentation\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr><td class=\"\" style=\"vertical-align:top;width:550px;\" ><![endif]-->\r\n <div class=\"mj-column-px-550 mj-outlook-group-fix\" style=\"font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;\">\r\n <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" style=\"vertical-align:top;\" width=\"100%\">\r\n <tbody>\r\n <tr>\r\n <td style=\"font-size:0px;word-break:break-word;\">\r\n <div style=\"height:20px;line-height:20px;\"> <\/div>\r\n <\/td>\r\n <\/tr>\r\n <tr>\r\n <td style=\"font-size:0px;word-break:break-word;\">\r\n <div style=\"height:20px;line-height:20px;\"> <\/div>\r\n <\/td>\r\n <\/tr>\r\n <tr>\r\n <td align=\"left\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\">\r\n <div style=\"font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:28px;font-weight:700;line-height:1;text-align:left;color:#000000;\">\r\n <p style=\"padding: 0; line-height: 1.4em; font-family: 'Open Sans', Helvetica, Arial, sans-serif; margin: 0;\">Hello World! <\/p>\r\n <\/div>\r\n <\/td>\r\n <\/tr>\r\n <tr>\r\n <td align=\"left\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\">\r\n <div style=\"font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:14px;line-height:1;text-align:left;color:#000000;\">\r\n <p style=\"padding: 0; line-height: 1.4em; font-family: 'Open Sans', Helvetica, Arial, sans-serif; margin: 0;\">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aliquid officia consequatur placeat reprehenderit excepturi, tempore, id quos quaerat ab fuga. <\/p>\r\n <p style=\"padding: 0; line-height: 1.4em; font-family: 'Open Sans', Helvetica, Arial, sans-serif; margin: 0;\">\r\n <br data-cke-filler=\"true\">\r\n <\/p>\r\n <p style=\"padding: 0; line-height: 1.4em; font-family: 'Open Sans', Helvetica, Arial, sans-serif; margin: 0;\">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore, voluptate. <\/p>\r\n <p style=\"padding: 0; line-height: 1.4em; font-family: 'Open Sans', Helvetica, Arial, sans-serif; margin: 0;\">\r\n <br data-cke-filler=\"true\">\r\n <\/p>\r\n <p style=\"padding: 0; line-height: 1.4em; font-family: 'Open Sans', Helvetica, Arial, sans-serif; margin: 0;\">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dignissimos alias rerum nemo ducimus modi perspiciatis. <\/p>\r\n <\/div>\r\n <\/td>\r\n <\/tr>\r\n <tr>\r\n <td style=\"font-size:0px;word-break:break-word;\">\r\n <div style=\"height:20px;line-height:20px;\"> <\/div>\r\n <\/td>\r\n <\/tr>\r\n <tr>\r\n <td align=\"left\" style=\"font-size:0px;padding:10px 25px;word-break:break-word;\">\r\n <div style=\"font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:11px;line-height:1;text-align:left;color:#6d6d6d;\">\r\n <p style=\"padding: 0; line-height: 1.4em; font-family: 'Open Sans', Helvetica, Arial, sans-serif; margin: 0;\">{unsubscribe_text} | {webview_text}<\/p>\r\n <\/div>\r\n <\/td>\r\n <\/tr>\r\n <tr>\r\n <td style=\"font-size:0px;word-break:break-word;\">\r\n <div style=\"height:20px;line-height:20px;\"> <\/div>\r\n <\/td>\r\n <\/tr>\r\n <tr>\r\n <td style=\"font-size:0px;word-break:break-word;\">\r\n <div style=\"height:20px;line-height:20px;\"> <\/div>\r\n <\/td>\r\n <\/tr>\r\n <\/tbody>\r\n <\/table>\r\n <\/div>\r\n <!--[if mso | IE]><\/td><\/tr><\/table><![endif]-->\r\n <\/td>\r\n <\/tr>\r\n <\/tbody>\r\n <\/table>\r\n <\/div>\r\n <!--[if mso | IE]><\/td><\/tr><\/table><![endif]-->\r\n <\/div>\r\n<\/body>\r\n\r\n<\/html>", + "email_type": "template", + "publish_up": null, + "publish_down": null, + "revision": 5, + "lang": "en", + "variant_settings": [], + "variant_start_date": null, + "dynamic_content": [ + { + "tokenName": "Dynamic Content 1", + "content": "Default Dynamic Content", + "filters": [ + { + "content": null, + "filters": [] + } + ] + } + ], + "headers": [], + "public_preview": false, + "uuid": "e9315582-087e-42f1-a9c5-3af468fd522c" + } + ], + "lists": [ + { + "id": 1, + "name": "Test Seg", + "is_published": true, + "description": null, + "alias": "test-seg", + "public_name": "Test Seg", + "filters": [], + "is_global": true, + "is_preference_center": false, + "uuid": "d697157e-9ae3-4600-aa2e-4a2a5a6e36e0" + } + ], + "forms": [ + { + "id": 1, + "name": "test form", + "is_published": true, + "description": null, + "alias": "test_form", + "lang": null, + "cached_html": "\n<style type=\"text\/css\" scoped>\n .mauticform_wrapper { max-width: 600px; margin: 10px auto; }\n .mauticform-innerform {}\n .mauticform-post-success {}\n .mauticform-name { font-weight: bold; font-size: 1.5em; margin-bottom: 3px; }\n .mauticform-description { margin-top: 2px; margin-bottom: 10px; }\n .mauticform-error { margin-bottom: 10px; color: red; }\n .mauticform-message { margin-bottom: 10px; color: green; }\n .mauticform-row { display: block; margin-bottom: 20px; }\n .mauticform-label { font-size: 1.1em; display: block; font-weight: bold; margin-bottom: 5px; }\n .mauticform-row.mauticform-required .mauticform-label:after { color: #e32; content: \" *\"; display: inline; }\n .mauticform-helpmessage { display: block; font-size: 0.9em; margin-bottom: 3px; }\n .mauticform-errormsg { display: block; color: red; margin-top: 2px; }\n .mauticform-selectbox, .mauticform-input, .mauticform-textarea { width: 100%; padding: 0.5em 0.5em; border: 1px solid #CCC; background: #fff; box-shadow: 0px 0px 0px #fff inset; border-radius: 4px; box-sizing: border-box; }\n .mauticform-checkboxgrp-row {}\n .mauticform-checkboxgrp-label { font-weight: normal; }\n .mauticform-checkboxgrp-checkbox {}\n .mauticform-radiogrp-row {}\n .mauticform-radiogrp-label { font-weight: normal; }\n .mauticform-radiogrp-radio {}\n .mauticform-button-wrapper .mauticform-button.btn-ghost, .mauticform-pagebreak-wrapper .mauticform-pagebreak.btn-ghost { color: #5d6c7c;background-color: #ffffff;border-color: #dddddd;}\n .mauticform-button-wrapper .mauticform-button, .mauticform-pagebreak-wrapper .mauticform-pagebreak { display: inline-block;margin-bottom: 0;font-weight: 600;text-align: center;vertical-align: middle;cursor: pointer;background-image: none;border: 1px solid transparent;white-space: nowrap;padding: 6px 12px;font-size: 13px;line-height: 1.3856;border-radius: 3px;-webkit-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none;}\n .mauticform-button-wrapper .mauticform-button.btn-ghost[disabled], .mauticform-pagebreak-wrapper .mauticform-pagebreak.btn-ghost[disabled] { background-color: #ffffff; border-color: #dddddd; opacity: 0.75; cursor: not-allowed; }\n .mauticform-pagebreak-wrapper .mauticform-button-wrapper { display: inline; }\n\n \/* Make fields display inline when using width classes *\/\n .mauticform-page-wrapper {\n display: flex;\n flex-wrap: wrap;\n width: 100%;\n margin: 0 -10px;\n }\n\n \/* Ensure field containers respect width classes *\/\n .mauticform-row {\n box-sizing: border-box;\n padding: 0 10px;\n margin-bottom: 15px;\n }\n\n \/* Responsive adjustment for mobile *\/\n @media (max-width: 767px) {\n .mauticform-three-quarters-width,\n .mauticform-two-thirds-width,\n .mauticform-half-width,\n .mauticform-one-third-width,\n .mauticform-one-quarter-width {\n width: 100%;\n }\n }\n\n \/**\n * @see https:\/\/github.com\/TarekRaafat\/autoComplete.js\/blob\/master\/dist\/css\/autoComplete.02.css.\n *\/\n .autoComplete_wrapper {position: relative;}\n .autoComplete_wrapper > input::placeholder {transition: all 0.3s ease;}\n .autoComplete_wrapper > ul {position: absolute;max-height: 226px;overflow-y: scroll;top: 100%;left: 0;right: 0;padding: 0;margin: 0.5rem 0 0 0;border-radius: 4px;background-color: #fff;border: 1px solid rgba(33, 33, 33, 0.1);z-index: 1000;outline: none;}\n .autoComplete_wrapper > ul > li {padding: 10px 20px;list-style: none;text-align: left;font-size: 16px;color: #212121;transition: all 0.1s ease-in-out;border-radius: 3px;background-color: rgba(255, 255, 255, 1);white-space: nowrap;overflow: hidden;text-overflow: ellipsis;transition: all 0.2s ease;}\n .autoComplete_wrapper > ul > li > span {float: right;}\n .autoComplete_wrapper > ul > li::selection {color: rgba(#ffffff, 0);background-color: rgba(#ffffff, 0);}\n .autoComplete_wrapper > ul > li:hover {cursor: pointer;background-color: rgba(123, 123, 123, 0.1);}\n .autoComplete_wrapper > ul > li mark {background-color: transparent;font-weight: bold;}\n .autoComplete_wrapper > ul > li mark::selection {background-color: rgba(#ffffff, 0);}\n .autoComplete_wrapper > ul > li[aria-selected=\"true\"] {background-color: rgba(123, 123, 123, 0.1);}\n @media only screen and (max-width: 600px) {\n .autoComplete_wrapper > input {width: 18rem;}\n }\n<\/style>\n\n<style type=\"text\/css\" scoped>\n .mauticform-field-hidden { display:none }\n<\/style>\n<div id=\"mauticform_wrapper_testform\" class=\"mauticform_wrapper\">\n <form autocomplete=\"false\" role=\"form\" method=\"post\" action=\"https:\/\/mautic.ddev.site\/form\/submit?formId=1\" id=\"mauticform_testform\" data-mautic-form=\"testform\" enctype=\"multipart\/form-data\" ><div class=\"mauticform-error\" id=\"mauticform_testform_error\"><\/div>\n <div class=\"mauticform-message\" id=\"mauticform_testform_message\"><\/div><div class=\"mauticform-innerform\">\n <div class=\"mauticform-page-wrapper mauticform-page-1\" data-mautic-form-page=\"1\" >\n \n \n \n \n \n\n<div id=\"mauticform_testform_submit\"class=\"mauticform-row mauticform-button-wrapper mauticform-field-1\"style=\"width: 100%\">\n <button class=\"btn btn-ghost mauticform-button\"name=\"mauticform[submit]\"value=\"1\"id=\"mauticform_input_testform_submit\"type=\"submit\">Submit<\/button>\n<\/div>\n <\/div><\/div><input type=\"hidden\" name=\"mauticform[formId]\" id=\"mauticform_testform_id\" value=\"1\"\/>\n <input type=\"hidden\" name=\"mauticform[return]\" id=\"mauticform_testform_return\" value=\"\"\/>\n <input type=\"hidden\" name=\"mauticform[formName]\" id=\"mauticform_testform_name\" value=\"testform\"\/>\n \n <\/form>\n<\/div>\n", + "post_action": "return", + "template": null, + "form_type": "campaign", + "render_style": true, + "post_action_property": null, + "form_attr": null, + "uuid": "c98d1a1b-1a12-4931-bb4d-3583394cd573" + } + ], + "form_fields": [ + { + "id": 1, + "uuid": "74f101ad-94b6-4013-bd7d-9dbba719637a", + "label": "Submit", + "show_label": true, + "alias": "submit", + "type": "button", + "is_custom": false, + "custom_parameters": [], + "default_value": null, + "is_required": false, + "validation_message": null, + "help_message": null, + "field_order": 1, + "properties": [], + "validation": [], + "parent_id": null, + "conditions": [], + "label_attr": null, + "input_attr": "class=\"btn btn-ghost\"", + "container_attr": null, + "save_result": true, + "is_auto_fill": false, + "show_when_value_exists": null, + "show_after_x_submissions": null, + "mapped_object": null, + "mapped_field": null, + "form": 1 + } + ], + "dependencies": [ + { + "campaign_event": [ + { + "campaign": 3, + "campaign_event": 5, + "asset": 2 + }, + { + "campaign": 3, + "campaign_event": 7, + "dynamicContent": 1 + }, + { + "campaign": 3, + "campaign_event": 8, + "page": 1 + }, + { + "campaign": 3, + "campaign_event": 9, + "pointGroup": 1 + }, + { + "campaign": 3, + "campaign_event": 6, + "email": 1 + } + ], + "lists": [ + { + "campaign": 3, + "lists": 1 + } + ], + "forms": [ + { + "forms": 1, + "form_fields": 1 + }, + { + "campaign": 3, + "forms": 1 + } + ] + } + ] + } +] From fbef5c6a93e2437bfcc323e4d43e23e6324abcd4 Mon Sep 17 00:00:00 2001 From: Matt O'Keefe <Matt@OKeefe.dev> Date: Wed, 30 Apr 2025 09:19:57 -0500 Subject: [PATCH 05/28] clarify messenger:consume params --- docs/configuration/cron_jobs.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration/cron_jobs.rst b/docs/configuration/cron_jobs.rst index 239378b4..3012306e 100644 --- a/docs/configuration/cron_jobs.rst +++ b/docs/configuration/cron_jobs.rst @@ -112,7 +112,7 @@ Process Email queue cron job .. vale on -If the system configuration is queueing Emails, a cron job processes them. +If the system configuration is queueing Emails, a cron job processes them. If you plan to run `messenger:consume` on a cron you should include, at minimum one of these parameters `--memory-limit`, `--limit` (number of emails), `--time-limit` otherwise, this will start up a long-lived process. .. code-block:: php From b64cf46995487d0b4721f21164644b1005c5360c Mon Sep 17 00:00:00 2001 From: Matt O'Keefe <Matt@OKeefe.dev> Date: Wed, 30 Apr 2025 09:21:30 -0500 Subject: [PATCH 06/28] Update cron_jobs.rst --- docs/configuration/cron_jobs.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration/cron_jobs.rst b/docs/configuration/cron_jobs.rst index 3012306e..29e61c96 100644 --- a/docs/configuration/cron_jobs.rst +++ b/docs/configuration/cron_jobs.rst @@ -116,7 +116,7 @@ If the system configuration is queueing Emails, a cron job processes them. If yo .. code-block:: php - php /path/to/mautic/bin/console messenger:consume email + php /path/to/mautic/bin/console messenger:consume email --time-limit=160 .. vale off From bfa6d5039e80d35310f8cff2b706962aced0c89e Mon Sep 17 00:00:00 2001 From: Matt O'Keefe <Matt@OKeefe.dev> Date: Wed, 30 Apr 2025 09:41:55 -0500 Subject: [PATCH 07/28] Update cron_jobs.rst passive -> active... hopefully --- docs/configuration/cron_jobs.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration/cron_jobs.rst b/docs/configuration/cron_jobs.rst index 29e61c96..f41005d8 100644 --- a/docs/configuration/cron_jobs.rst +++ b/docs/configuration/cron_jobs.rst @@ -112,7 +112,7 @@ Process Email queue cron job .. vale on -If the system configuration is queueing Emails, a cron job processes them. If you plan to run `messenger:consume` on a cron you should include, at minimum one of these parameters `--memory-limit`, `--limit` (number of emails), `--time-limit` otherwise, this will start up a long-lived process. +If the system configuration queues emails, a cron job processes them. If you plan to run `messenger:consume` using a Cron job, you should include at least one of these parameters: `--memory-limit`, `--limit` (number of emails), or `--time-limit`; otherwise, the command starts a long-lived process. .. code-block:: php From c274d666ba5386a3c2ca719740e7580adf5f949c Mon Sep 17 00:00:00 2001 From: Matt O'Keefe <Matt@OKeefe.dev> Date: Wed, 30 Apr 2025 09:45:24 -0500 Subject: [PATCH 08/28] Update cron_jobs.rst --- docs/configuration/cron_jobs.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration/cron_jobs.rst b/docs/configuration/cron_jobs.rst index f41005d8..36009908 100644 --- a/docs/configuration/cron_jobs.rst +++ b/docs/configuration/cron_jobs.rst @@ -112,7 +112,7 @@ Process Email queue cron job .. vale on -If the system configuration queues emails, a cron job processes them. If you plan to run `messenger:consume` using a Cron job, you should include at least one of these parameters: `--memory-limit`, `--limit` (number of emails), or `--time-limit`; otherwise, the command starts a long-lived process. +If the system configuration queues emails, a cron job processes them. If you plan to run `messenger:consume` using a Cron job, you should include at least one of these parameters: `--memory-limit`, `--limit` - the number of emails - or, `--time-limit`; otherwise, the command starts a long-lived process. .. code-block:: php From a044ec14256952811fe2f37b321bc39c080a08f0 Mon Sep 17 00:00:00 2001 From: Matt O'Keefe <Matt@OKeefe.dev> Date: Wed, 30 Apr 2025 09:49:24 -0500 Subject: [PATCH 09/28] Update cron_jobs.rst --- docs/configuration/cron_jobs.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration/cron_jobs.rst b/docs/configuration/cron_jobs.rst index 36009908..6af22569 100644 --- a/docs/configuration/cron_jobs.rst +++ b/docs/configuration/cron_jobs.rst @@ -112,7 +112,7 @@ Process Email queue cron job .. vale on -If the system configuration queues emails, a cron job processes them. If you plan to run `messenger:consume` using a Cron job, you should include at least one of these parameters: `--memory-limit`, `--limit` - the number of emails - or, `--time-limit`; otherwise, the command starts a long-lived process. +If the system configuration queues Emails, a Cron job processes them. If you plan to run `messenger:consume` using a Cron job, you should include at least one of these parameters: `--memory-limit`, `--limit` - the number of emails - or, `--time-limit`; otherwise, the command starts a long-lived process. .. code-block:: php From bdb0fb9277235f13a9731406733f896fc4066bb1 Mon Sep 17 00:00:00 2001 From: Matt O'Keefe <Matt@OKeefe.dev> Date: Wed, 30 Apr 2025 13:03:09 -0500 Subject: [PATCH 10/28] Update docs/configuration/cron_jobs.rst Co-authored-by: Ruth Cheesley <ruth@ruthcheesley.co.uk> --- docs/configuration/cron_jobs.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration/cron_jobs.rst b/docs/configuration/cron_jobs.rst index 6af22569..2a3862ee 100644 --- a/docs/configuration/cron_jobs.rst +++ b/docs/configuration/cron_jobs.rst @@ -114,7 +114,7 @@ Process Email queue cron job If the system configuration queues Emails, a Cron job processes them. If you plan to run `messenger:consume` using a Cron job, you should include at least one of these parameters: `--memory-limit`, `--limit` - the number of emails - or, `--time-limit`; otherwise, the command starts a long-lived process. -.. code-block:: php +.. code-block:: bash php /path/to/mautic/bin/console messenger:consume email --time-limit=160 From 7ddbe753b59c5e2c209a7a82a030fad6c5a75b2e Mon Sep 17 00:00:00 2001 From: Matt O'Keefe <Matt@OKeefe.dev> Date: Mon, 5 May 2025 05:28:20 -0500 Subject: [PATCH 11/28] Update docs/configuration/cron_jobs.rst --- docs/configuration/cron_jobs.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration/cron_jobs.rst b/docs/configuration/cron_jobs.rst index 2a3862ee..9aded6f3 100644 --- a/docs/configuration/cron_jobs.rst +++ b/docs/configuration/cron_jobs.rst @@ -112,7 +112,7 @@ Process Email queue cron job .. vale on -If the system configuration queues Emails, a Cron job processes them. If you plan to run `messenger:consume` using a Cron job, you should include at least one of these parameters: `--memory-limit`, `--limit` - the number of emails - or, `--time-limit`; otherwise, the command starts a long-lived process. +If the system configuration queues Emails, a Cron job processes them. If you plan to run ``messenger:consume`` using a Cron job, you should include at least one of these parameters: ``--memory-limit``, ``--limit`` - the number of emails, or ``--time-limit``. This starts a long-lived process and continues to run without one of these parameters. .. code-block:: bash From 54669dda2b4532132de222b45d275c78af1a7679 Mon Sep 17 00:00:00 2001 From: David Jarvis aka DJ <dcjarvis@yahoo.com> Date: Tue, 20 May 2025 11:49:33 +0100 Subject: [PATCH 12/28] updates from prose review --- .DS_Store | Bin 6148 -> 6148 bytes docs/.DS_Store | Bin 6148 -> 6148 bytes docs/campaigns/.DS_Store | Bin 6148 -> 8196 bytes docs/campaigns/exporting_campaigns.rst | 14 +- docs/campaigns/importing_campaigns.rst | 12 +- docs/configuration/command_line_interface.rst | 11 +- docs/some-dir/README.md | 152 ++++++++++++++++ docs/some-dir/composer.json | 168 ++++++++++++++++++ 8 files changed, 342 insertions(+), 15 deletions(-) create mode 100644 docs/some-dir/README.md create mode 100644 docs/some-dir/composer.json diff --git a/.DS_Store b/.DS_Store index 9c5665fae40a518ff995e97b94abb301051d93eb..4f798cb0e7d91d5504a7935b56e3cc26312dc338 100644 GIT binary patch delta 85 zcmZoMXfc?O%(!DRBh&K91|lqz`58rkJQc=5b&2X~LrV)&9R(xfS{;RI3rir=+|01H nmXkwNS>HM+K07BjFTZQ@0Y-Vo?#&Mv)!8OCuxw`M_{$Ff5?C1n delta 86 zcmZoMXfc?O%(!PVBh&K91|lqz`58rkJQc=5ZJFw7LrV)&9R*!eQ`1@<g=#}{OA8$Z pLt|sh+FDKyQDuGWp!n>Z+`Rn0$$J>(8T&WiVN_?^%+B$b9{?Ne7|H+u diff --git a/docs/.DS_Store b/docs/.DS_Store index 35bd5f33d586bf290c70a6b9385d50c69e665784..da52a0dc890964b4156f3522b45752afb511257f 100644 GIT binary patch delta 128 zcmZoMXffEJ%EY*1vK(`vx<qxgp{0eXj)IYKt&T#qg(Z+_Zf00p%gG_CtZy9@pPiGN ym)|vc7qdKL_vA-R)(Cwj=Egb-h8C6(eL%LUr73oOn{P4evP^8?*v!uHmmdHqY$b;P delta 133 zcmZoMXffEJ%EY*5vJ`WnwoG-kp{0eXj)JbKscEf_Lbai}rG<`yp|P=LZ7nB<sItCw jP<(byZeD)h<b6!?jQx}EF<FzVck?A?U6#%49Dn%%vWg}d diff --git a/docs/campaigns/.DS_Store b/docs/campaigns/.DS_Store index a0560a6cbe79e5d2d6b6b5aa977abb7764d93322..0a8295168b8722aa9e565b22d991a52ba1538bbe 100644 GIT binary patch literal 8196 zcmeHMU2GIp6u#fI(3vTfDYj5{1U57RmaVkl0xcM}f5?vrvMntITxWNNGGRJXc4l`$ zLTog?7=99s@k#uDl*AZ)Fc85PiEo;!@M0n+zUYH7>XQkcJ9kRxLLYcB80IE(&zyVD znS1YdzcY8wEMpA4C9{*U7RH!N7nfQ+Rd*@8pI>(rDQc-D3G!#mVt&5jPvjjhe}{G4 z5F-#H5F-#H5F-#Ha6ceGXErbLEa$%Hjq4bJ7=Z^80sekS(#2&al9NJ)uMVoh6o4d8 z0m4G}lm~=9$w+1*IVoi5O3xJ810q*MDF%c)^(VPG$xI|Cg%s`#!kr-+8BvCUaCGuZ zhUN?zA>%qmAV%O$1bFz&Wi}gU1{<r&-!JAT9NP~%I=+WgQ#)sFU7b=_Uw0^b#H(Zj zBPjX3M)3gG20hQoPv0B)eYRD}X-$2e8yL206+;Kxw8-S(u<e>&rPnLDrXTudMTeru zN=|#^<jKyijp<afJ)KUUN+nNrZs^*WN_MVW*LCWYA~(0L*|axz)ERfZvtlWTUI(n9 zYH8=H*X8)-KJi_mCNaIPYtg!7MXlF|NA{~ZZ9$nPzk4FEJ=fo76@8L9ZN5*7++Oh9 zJw=Q64;MVAH17I2t+8M@g_2_gR*&Ok?H4S6B(RPL{BG3uia}p!IAW78Z_8;~$+z}e z<Y1Sresc4hswNKQ!o21s%U7)KNOy1P-@fbFsfLH<YmEz3b(D~D1N*RT6^AOe864Sf z_yx-~ZFg+AZ2NZJu?AgZ+-j`-Qc<QF7A|U8oY3{zOh-z2`&h{iPH3&N9FDPcnXVo* zX->O5laFF*pUU(c8ohOyt{>n%eMO6EGjdqDN>>McT9#%+hN*T{9Xv`~toeKj<l4Db z)rTA-Z#kN&h;i0$Q1#)mz*gHCHE!Ch>LUeje4Li(HzE}^XL@w~IX?2!h9B&nuw0te zm1y{Zt-3zyA2Ev7?h3I?yBf9cp!RUjsE4}^n0DY5c_%^g3@v3pe_)Skh6`y~xTrzT z>O->jtxVfvyJefWP1`OgF1Kdd#14hnwq`nnrqFq{m_1IX(>8XH;%0)KV5iwz>^%F3 zea5b^uh=*2I{T6R#BQ*k*>CI*_77^&fcenShy+&RF|=Vdo<IgY=*3R#!CquBh(pN3 zgoVTKP(lUAaRM*lRh-5doW+}X2k+wpe25FUgfDOzSMW8y!w<NI>$rhG@fZHaO-Yed zsa|T5nx#cji?muwNolED>JyWfrlaP-O!7(nq|bO8P}D?U^Q2>Ui<-pNr?%Z6QFDEs zh?9q#nwPe&YG1p4Q|1<*1OcZxcRQ0q`pzJINZ)X;t9Umh0!m#n9c<N+HAK9(j?0|9 zTAi>?s>@~Ky*5XvkE$zWV!Sq2sBMXqOfjZOp{lM)bSe}{T8&WK5@|)&Ae4=2w?Z+c z)e7}Vb&JBoTw|f0=uPy?6c?I&?{xi`ea<ekt89ws`V0G&{myPeCXy~h5-W(NoTTko zj}7QTH#TDcTd@t>iLiqh!Z1cKiUJY#Fveh`2p<8;coCCC+L!SPQT7~O!|Qkh=kYe) z#d{HXKf%Q+GVYj3MqH{UV>w%N9nT%3_GtpobcwZlLZC)ixA6RbVD|6-_w>xzI7T2w z;QxgHYWuVOy)@_9-l{y;PSW)}UA%C+Ng+cQs={=fWSEYVy!D46-6zSE`(z?HDI~Q} X`OiNDxbgXa`_I+*{10jPz*YYOdFLrv delta 205 zcmZp1XfcprU|?W$DortDU=RQ@Ie-{MGjdEU6q~50$jCG?VE1GL8J5ZX0uqe7CTj>3 zdP-GSo0uExD43d>*6Ju!8=6~M=qMN(8(Y@aa&m|&>strKXXoVR<@W&10U5;zp&59g zG>q!qyj(1Sabv?#mc{HG9D>XcaRF{1?Fw?i#=`H+llf&V6+jX|Lztj63y21CKyC#& LpJ8)6&m3j|$b=v$ diff --git a/docs/campaigns/exporting_campaigns.rst b/docs/campaigns/exporting_campaigns.rst index 99eec1ec..8e33429e 100644 --- a/docs/campaigns/exporting_campaigns.rst +++ b/docs/campaigns/exporting_campaigns.rst @@ -17,16 +17,16 @@ Once a campaign is selected, the export feature will extract all campaign data a - Dynamic content - Assets - - Custom fields + - Custom Fields - Other related dependencies The export command will: - - Detect use of plugins and custom fields + - Detect use of plugins and Custom Fields - Include the data to support these dependencies .. important:: - The importing instance will need the same custom fields and plugins to be present. + The importing instance will need the same Custom Fields and plugins to be present. Export Mechanic *************** @@ -63,14 +63,14 @@ Use the following commands: bin/console mautic:entity:export --entity=campaign --id=1 --zip-file * `entity` defines the type of entity to export, in this case `campaign` -* `id` defines the id of the campaign to export. Look at the URL to find the ID when you view or edit the campaign - the ID will appear in the URL e.g., /s/campaigns/view/123 where 123 is the ID +* `id` defines the id of the campaign to export. Look at the URL to find the ID when you view or edit the campaign - the ID will appear in the URL for example, /s/campaigns/view/123 where 123 is the ID * `zip-file` creates a zip file of the campaign and its dependencies .. code-block:: bash bin/console mautic:entity:export --entity=campaign --id=1 --json-file -* Creates only a JSON file and ignores any additional assets and files +* Creates only a JSON file and ignores any additional assets **3. API-Based Export** ----------------------- @@ -95,7 +95,7 @@ Python Example # API Endpoint campaign_id = 1 - url = f'https://{your-mautic-domain}/api/campaigns/export/{campaign_id}' + url = f'https://{your-domain}/api/campaigns/export/{campaign_id}' # Authentication headers = { @@ -111,7 +111,7 @@ Python Example # Save or process the exported campaign .. important:: - - Replace `{your-mautic-domain}` with your actual Mautic instance domain + - Replace `{your-domain}` with your actual Mautic instance domain - Replace `YOUR_ACCESS_TOKEN` with a valid authentication token - The API uses a GET request to export a specific campaign by ID - Ensure you have the necessary API permissions diff --git a/docs/campaigns/importing_campaigns.rst b/docs/campaigns/importing_campaigns.rst index d6f7ddba..75b86380 100644 --- a/docs/campaigns/importing_campaigns.rst +++ b/docs/campaigns/importing_campaigns.rst @@ -5,12 +5,12 @@ Importing Campaigns .. vale on -The Import feature allows you to add pre-configured campaigns to your Mautic instance using zip files containing all the relevant data to construct a campaign. +The Import feature allows you to add pre-configured Campaigns to your Mautic instance using zip files containing all the relevant data to construct a Campaign. Import Process -------------- -Step-by-Step Import +Step-by-step import ******************* 1. **Log into your Mautic account** @@ -26,14 +26,14 @@ Step-by-Step Import 4. **Select Campaign File** On the import screen: - - Choose the campaign zip file you wish to import + - Choose the Campaign zip file you wish to import - **Recommended:** Use a ZIP file created from the Mautic export function - - Ensures inclusion of campaign data, external assets, and dynamic content + - Ensures inclusion of Campaign data, external assets, and dynamic content .. important:: - Only ZIP files are supported for campaign imports using the UI. In addition, correctly structured JSON files can be used with both the command line and API endpoints. + Only ZIP files are supported for Campaign imports using the UI. In addition, correctly structured JSON files can be used with both the command line and API endpoints. -Import Mechanics +Import mechanics **************** During the import process, Mautic performs a comprehensive analysis of the data to be imported: diff --git a/docs/configuration/command_line_interface.rst b/docs/configuration/command_line_interface.rst index aaf15aa4..be435f3c 100644 --- a/docs/configuration/command_line_interface.rst +++ b/docs/configuration/command_line_interface.rst @@ -112,14 +112,21 @@ These are the commands you may need to use in relation to your Mautic instance. - * - ``mautic:email:fetch`` - Fetch and process monitored Email. - - + + * - ``mautic:entity:import --entity=campaign file=path-to-file/entity_data.zip`` + - Imports campaign and dependent entities to Mautic from a ZIP file. See :doc:`/campaigns/importing-campaigns`. + + * - ``mautic:entity:export --entity=campaign --id=1`` + - Exports campaign and dependent entities from Mautic to a ZIP file. See :doc:`/campaigns/exporting-campaigns`. + * - ``messenger:consume email`` - Processes mail queue - - * - ``mautic:fields:analse`` + * - ``mautic:fields:analyse`` - Analyze Custom Fields table and return table or file with results. See :doc:`/contacts/custom_fields`. - * - ``mautic:import`` + - Imports contacts from a CSV file - If the CSV import is configured to run in background then this command will pick up the pending import jobs and imports the data from CSV files to Mautic. - * - ``mautic:integration:fetchleads`` diff --git a/docs/some-dir/README.md b/docs/some-dir/README.md new file mode 100644 index 00000000..251971e0 --- /dev/null +++ b/docs/some-dir/README.md @@ -0,0 +1,152 @@ +# Composer template for Mautic projects + +This project template provides a starter kit for managing your Mautic +dependencies with [Composer](https://getcomposer.org/). + +## Usage + +First you need to [install composer 2](https://getcomposer.org/doc/00-intro.md#installation-linux-unix-osx). + +> Note: The instructions below refer to the [global composer installation](https://getcomposer.org/doc/00-intro.md#globally). +You might need to replace `composer` with `php composer.phar` (or similar) +for your setup. + +After that you can create the project: + +``` +composer create-project mautic/recommended-project:^5.0 some-dir --no-interaction +``` + +With `composer require ...` you can download new dependencies to your installation. + +Example of installing a plugin: +``` +cd some-dir +composer require mautic/helloworld-bundle +``` + +The `composer create-project` command passes ownership of all files to the +project that is created. You should create a new git repository, and commit +all files not excluded by the .gitignore file. + +## What does the template do? + +When installing the given `composer.json` some tasks are taken care of: + +* Mautic will be installed in the `docroot`-directory. +* Autoloader is implemented to use the generated composer autoloader in `vendor/autoload.php`, + instead of the one provided by Mautic (`docroot/vendor/autoload.php`). +* Plugins (packages of type `mautic-plugin`) will be placed in `docroot/plugins/` +* Themes (packages of type `mautic-theme`) will be placed in `docroot/themes/` +* Creates `docroot/media`-directory. +* Creates environment variables based on your .env file. See [.env.example](.env.example). + +## Updating Mautic Core + +This project will attempt to keep all of your Mautic Core files up-to-date; the +project [mautic/core-composer-scaffold](https://github.com/mautic/core-composer-scaffold) +is used to ensure that your scaffold files are updated every time mautic/core is +updated. If you customize any of the "scaffolding" files (commonly .htaccess), +you may need to merge conflicts if any of your modified files are updated in a +new release of Mautic core. + +Follow the steps below to update your core files. + +1. Run `composer update mautic/core --with-dependencies` to update Mautic Core and its dependencies. +2. Run `git diff` to determine if any of the scaffolding files have changed. + Review the files for any changes and restore any customizations to + `.htaccess` or others. +1. Commit everything all together in a single commit, so `docroot` will remain in + sync with the `core` when checking out branches or running `git bisect`. +1. In the event that there are non-trivial conflicts in step 2, you may wish + to perform these steps on a branch, and use `git merge` to combine the + updated core files with your customized files. This facilitates the use + of a [three-way merge tool such as kdiff3](http://www.gitshah.com/2010/12/how-to-setup-kdiff-as-diff-tool-for-git.html). This setup is not necessary if your changes are simple; + keeping all of your modifications at the beginning or end of the file is a + good strategy to keep merges easy. + +## FAQ + +### Should I commit the contributed plugins I download? + +Composer recommends **no**. They provide [argumentation against but also +workrounds if a project decides to do it anyway](https://getcomposer.org/doc/faqs/should-i-commit-the-dependencies-in-my-vendor-directory.md). + +### Should I commit the scaffolding files? + +The [Mautic Composer Scaffold](https://github.com/mautic/core-composer-scaffold) plugin can download the scaffold files (like +index.php, .htaccess, …) to the docroot/ directory of your project. If you have not customized those files you could choose +to not check them into your version control system (e.g. git). If that is the case for your project it might be +convenient to automatically run the mautic-scaffold plugin after every install or update of your project. You can +achieve that by registering `@composer mautic:scaffold` as post-install and post-update command in your composer.json: + +```json +"scripts": { + "post-install-cmd": [ + "@composer mautic:scaffold", + "..." + ], + "post-update-cmd": [ + "@composer mautic:scaffold", + "..." + ] +}, +``` +### How can I apply patches to downloaded plugins? + +If you need to apply patches (depending on the project being modified, a pull +request is often a better solution), you can do so with the +[composer-patches](https://github.com/cweagans/composer-patches) plugin. + +To add a patch to Mautic plugin foobar insert the patches section in the extra +section of composer.json: +```json +"extra": { + "patches": { + "mautic/foobar": { + "Patch description": "URL or local path to patch" + } + } +} +``` + +### How do I specify a PHP version ? + +This project supports PHP 7.4 as minimum version, however it's possible that a `composer update` will upgrade some package that will then require PHP 7+ or 8+. + +To prevent this you can add this code to specify the PHP version you want to use in the `config` section of `composer.json`: +```json +"config": { + "sort-packages": true, + "platform": { + "php": "7.4" + } +}, +``` + +### How do I use another folder than docroot as webroot + +By default the composer.json file is configures to put all Mautic core, plugin and theme files in the `docroot` folder. +It is possible to change this folder to your own needs. + +In following examples, we will change `docroot` into `public`. + +##### New installations + +* Run the `create-project` command without installing + ```bash + composer create-project mautic/recommended-project:^4.0 some-dir --no-interaction --no-install + ``` +* Do a find and replace in the `composer.json` file to change `docroot/` into `public/`. +* Review the changes in the `composer.json` file to ensure there are no unintentional replacements. +* Run `composer install` to install all dependencies in the correct location. + +##### Existing installations + +* move the `docroot/` to `public/` + ```bash + mv docroot public + ``` +* Do a find and replace in the `composer.json` file to change `docroot/` into `public/`. +* review the changes in the `composer.json` file to ensure there are no unintentional replacements. +* run `composer update --lock` to ensure the autoloader is aware of the changed folder. diff --git a/docs/some-dir/composer.json b/docs/some-dir/composer.json new file mode 100644 index 00000000..d8efdcd0 --- /dev/null +++ b/docs/some-dir/composer.json @@ -0,0 +1,168 @@ +{ + "name": "mautic/recommended-project", + "description": "Project template for Mautic 5 projects with composer", + "type": "project", + "license": "GPL-2.0-or-later", + "homepage": "https://www.mautic.org/mautic-releases", + "support": { + "user-docs": "https://docs.mautic.org/en", + "developer-docs": "https://developer.mautic.org", + "chat": "https://www.mautic.org/slack" + }, + "funding": [ + { + "type": "other", + "url": "https://opencollective.com/mautic" + }, + { + "type": "other", + "url": "https://github.com/sponsors/mautic" + } + ], + "authors": [ + { + "name": "", + "role": "" + } + ], + "require": { + "composer/installers": "^1.11", + "mautic/core-composer-scaffold": "4.x-dev", + "mautic/core-project-message": "4.x-dev", + "mautic/core-lib": "5.2.5", + "mautic/grapes-js-builder-bundle": "5.2.5", + "mautic/plugin-clearbit": "5.2.5", + "mautic/plugin-cloudstorage": "5.2.5", + "mautic/plugin-crm": "5.2.5", + "mautic/plugin-emailmarketing": "5.2.5", + "mautic/plugin-focus": "5.2.5", + "mautic/plugin-fullcontact": "5.2.5", + "mautic/plugin-gmail": "5.2.5", + "mautic/plugin-outlook": "5.2.5", + "mautic/plugin-social": "5.2.5", + "mautic/plugin-tagmanager": "5.2.5", + "mautic/plugin-zapier": "5.2.5", + "mautic/theme-aurora": "5.2.5", + "mautic/theme-blank": "5.2.5", + "mautic/theme-brienz": "5.2.5", + "mautic/theme-cards": "5.2.5", + "mautic/theme-confirmme": "5.2.5", + "mautic/theme-fresh-center": "5.2.5", + "mautic/theme-fresh-fixed": "5.2.5", + "mautic/theme-fresh-left": "5.2.5", + "mautic/theme-fresh-wide": "5.2.5", + "mautic/theme-goldstar": "5.2.5", + "mautic/theme-neopolitan": "5.2.5", + "mautic/theme-oxygen": "5.2.5", + "mautic/theme-paprika": "5.2.5", + "mautic/theme-skyline": "5.2.5", + "mautic/theme-sparse": "5.2.5", + "mautic/theme-sunday": "5.2.5", + "mautic/theme-vibrant": "5.2.5", + "mautic/theme-trulypersonal": "5.2.5", + "mautic/theme-1-2-1-2-column": "5.2.5", + "mautic/theme-1-2-1-column": "5.2.5", + "mautic/theme-1-2-column": "5.2.5", + "mautic/theme-1-3-1-3-column": "5.2.5", + "mautic/theme-1-3-column": "5.2.5", + "mautic/theme-attract": "5.2.5", + "mautic/theme-connect-through-content": "5.2.5", + "mautic/theme-creative": "5.2.5", + "mautic/theme-educate": "5.2.5", + "mautic/theme-gallery": "5.2.5", + "mautic/theme-make-announcement": "5.2.5", + "mautic/theme-showcase": "5.2.5", + "mautic/theme-simple-text": "5.2.5", + "mautic/theme-survey": "5.2.5", + "mautic/theme-welcome": "5.2.5" + }, + "repositories": [ + { + "type": "git", + "url": "https://github.com/mautic/FOSOAuthServerBundle.git" + }, + { + "type": "git", + "url": "https://github.com/mautic/SpBundle.git" + }, + { + "type": "git", + "url": "https://github.com/mautic/SymfonyBridgeBundle.git" + } + ], + "conflict": { + "mautic/mautic": "*", + "mautic/core": "*" + }, + "minimum-stability": "dev", + "prefer-stable": true, + "config": { + "sort-packages": true, + "allow-plugins": { + "composer/installers": true, + "symfony/flex": true, + "mautic/core-composer-scaffold": true, + "mautic/core-project-message": true, + "php-http/discovery": true + } + }, + "autoload": { + "psr-4": { + "MauticPlugin\\": "docroot/plugins/" + } + }, + "scripts": { + "post-install-cmd": [ + "@npm-ci", + "@npx-patch-package", + "@generate-assets" + ], + "post-update-cmd": [ + "@npm-ci", + "@npx-patch-package", + "@generate-assets" + ], + "npm-ci": "npm ci --prefer-offline --no-audit", + "npx-patch-package": "npx patch-package", + "generate-assets": "@php bin/console mautic:assets:generate" + }, + "extra": { + "mautic-scaffold": { + "locations": { + "web-root": "docroot/" + } + }, + "installer-paths": { + "docroot/app": [ + "type:mautic-core" + ], + "docroot/plugins/{$name}": [ + "type:mautic-plugin" + ], + "docroot/themes/{$name}": [ + "type:mautic-theme" + ] + }, + "mautic-core-project-message": { + "include-keys": [ + "homepage", + "support" + ], + "post-create-project-cmd-message": [ + "<bg=blue;fg=white> </>", + "<bg=blue;fg=white> Congratulations, you’ve installed the Mautic codebase </>", + "<bg=blue;fg=white> from the mautic/recommended-project template! </>", + "<bg=blue;fg=white> </>", + "", + "<bg=yellow;fg=black>Next steps</>:", + " * Install Mautic", + " * Read the user guide", + " * Get support: https://www.mautic.org/support", + " * Get involved with the Mautic community:", + " https://www.mautic.org/getting-involved", + " * Remove the plugin that prints this message:", + " composer remove mautic/core-project-message" + ] + } + } +} From b50e7c93808ff0b929d6e60de91d9fb886243ba3 Mon Sep 17 00:00:00 2001 From: David Jarvis aka DJ <dcjarvis@yahoo.com> Date: Tue, 20 May 2025 11:50:12 +0100 Subject: [PATCH 13/28] updates from prose review --- .DS_Store | Bin 6148 -> 8196 bytes docs/.DS_Store | Bin 6148 -> 6148 bytes docs/some-dir/README.md | 152 -------------------------------- docs/some-dir/composer.json | 168 ------------------------------------ 4 files changed, 320 deletions(-) delete mode 100644 docs/some-dir/README.md delete mode 100644 docs/some-dir/composer.json diff --git a/.DS_Store b/.DS_Store index 4f798cb0e7d91d5504a7935b56e3cc26312dc338..2fc29c834656c1b3964a9bdf9b62442724b37d06 100644 GIT binary patch literal 8196 zcmeHMTWl0n7(U;$&>1?=0gAM^6BY_V$O0|5mYdn$lzV|~>9$;!*`1LNOlQi@?6y!E z8xvpTVtmqg=SicH2Ls+-d{7a6R5ZaDABY<D1rvQydGMb(v!oZA_+X6UoaCH;{{Q^{ zIrIPL`_I`k%NRpTL2qEJhA}2m^{G-x&0UJ$@7GC12qoo2LG~;==niBpCrkXhle8m2 z6oDuLQ3Rq0L=lK0@Nb9!o!Nd7r#Sa{Z`4K+h$8U6jDWvC#Hso;8PZWsgRO&_;0i$0 zzXAk>o)!OrXgs9JkdAUnJygI=iE>kfR}64-lE*{6WJpIj<>n0G<pbf&2yZ9|W~cGv zfjUEy)2NLi5Jg~e1pM?VXF79Oj=5v+@7V#%biMleFCmnbPn%v*AyrgX98UE)gDFq* z3T}&*Kj?Fvj$>s@&sz3?X$)qR>Q=}0G}AWnfq|(Tq;ja+v~_2&#VOdj8`!3Jha`zo zMyVYhZfIK9kch8byI~{|A8y*XJ`r!+&@eJ0iSz1KZ{D9dVfEY2S?&YEeSpoJ;M2LW z4q0xom4A>ZOO+h@Ds)JcW>m^E)$Vjp??E}E%qh~FcMN!@W4i~8yh}8rRJr8g_MBty z%^QAych0d2{kEG?X6H03SFkkCXtu1BdD<XZm7B{umbKS$O@El4v}X)?e&3|7bg%2= zz1Co`v`OQ0Ev4WZ`wbe@EE;Vzv|ByFIY^gg&0o0Wft70-w<O!Q@7{O4N|`-pZnZ4; zQYdZDe8e>JU4y3X_4R0O&aidUKGt0{T{CMLown9*%tk_zu2<F6F1SCYs#B?^3t97I z!Sqfkb)p!|5nrUrhje<U-Hz_hqAOQK8i!_Ix=2+I`aP|AgIYZ_SiVA)J6&>K(L%*l zt7W<K1YtmN{Z}B<wd-WH%hIxjrRWl$r)i_Cb{Bd4m7Sq*YqhMVb54Ihx#)(n<`z{w z;!nFvbG?oM!=_id7S8<;_4T@aTHfdwBn*bq9V88wl;plqyKZ_;&`HtQLtgaw!!_%A z;KPEN+A1}rc8SVoVn$I+yJ!;bX<0e^#k%By(O`Azk}LQs)s+@22y|Vtp7Ro&Y${vB z+So33l;znlJI|hB7ujX@5xdI1VBfGG*)8@Pfa#Dh1C^-80xUrS>(PiNY(xt>un+r@ z!Xfm*#BsP7!f6cS3?9c5IEVANfT!^SUc$?`h}ZBYF5?Q`!uz;}Pw*)|$4z{T@9;ft z;dlIj+rl(qzEC673X6q=uu@nhGzv|^CSkL%N9YjJf+idn9ASt%Ae2(=aLK_?ev0Uw zy@OIM{PrhYx_hd{9^STn$Ikyws@<5ylWX36^W#fbtX|i&wfPPRIciHl9Si>e^W!ol zz<hv-33$EEvrt}G%Fv1F*hR@=42gcuDivINvAjg2WKpJZcA30fq%={cb2brMB~oH4 zLcq!mv9%JVpHjxzM66K~6>w&gyhWl!RLVK4%1KF5Sir<uW9=d(lOq0ID8IpOvLD#b z>^7l%7UrS`%TQ0)eh}NRhtTa~y9-B<frcz}^uxvgP7=<~U<8k16p!I7;rs%g#8Y?{ z&*6Ezh*$6`Vf}TyK}dfa@8CUrfDdsMABUL#nh^gJewl#c-Q!V=U&o@jn9AFhV;>_~ z3H9UE?<KB`J5%G||J$ej{(mpI9QhJOAd0|*2%x+z)z(77skS`-t{ta(l&W8Ry-`jB t6KckN2q6B>ABOZCCsp4j8PZWsNkZix{}Aw(e`-hPe{}u_5WOdxKLPphXutpf delta 190 zcmZp1XfcprU|?W$DortDU=RQ@Ie-{MGjdEU6q~50D9QxlfW(r@iwlx+@{@r49TN+e zPd1QY5oKXWVaR7lW++CMoU9^H$SqM_ZD?s>s-s|JJb9sjG-KD~y#n%#-IMvme{O6r zVqMJ6!6C>DGztg=xPgQ#$jXg{-<c=#%UCLagn^cV>;};+AR5R4IS%ArhRyLjbC>}n CCnDPb diff --git a/docs/.DS_Store b/docs/.DS_Store index da52a0dc890964b4156f3522b45752afb511257f..170d3d735206dadf64d8bc1a1cffeeddd2e12359 100644 GIT binary patch delta 169 zcmZoMXfc=|#>B!ku~2NHo+2aL#(>?7iv?Ji7}+-SFui7!<zOgg$Y;o9NM+DvNMXoi zC`u|XE=bDBPhwzT*g08>xllv0y4ujf#85{;*VwF9N1@un(o{#m+|01HmXkwNS>HM+ zK07BjFTZ>8J|=n59-uZ1BR9WbR%6`E&cV+CbjW5#mha4y`9&N#7#SFtfXW#*M~JLp F1_17}C-MLQ delta 68 zcmZoMXfc=|#>B)qu~2NHo+2a5#(>?7j4YdZSY9)3{=g>AxUu0U(`I%Keh#3T&4L`? WnJ4p$IC3xm0V4wg%jO7?HOv5mm=N^< diff --git a/docs/some-dir/README.md b/docs/some-dir/README.md deleted file mode 100644 index 251971e0..00000000 --- a/docs/some-dir/README.md +++ /dev/null @@ -1,152 +0,0 @@ -# Composer template for Mautic projects - -This project template provides a starter kit for managing your Mautic -dependencies with [Composer](https://getcomposer.org/). - -## Usage - -First you need to [install composer 2](https://getcomposer.org/doc/00-intro.md#installation-linux-unix-osx). - -> Note: The instructions below refer to the [global composer installation](https://getcomposer.org/doc/00-intro.md#globally). -You might need to replace `composer` with `php composer.phar` (or similar) -for your setup. - -After that you can create the project: - -``` -composer create-project mautic/recommended-project:^5.0 some-dir --no-interaction -``` - -With `composer require ...` you can download new dependencies to your installation. - -Example of installing a plugin: -``` -cd some-dir -composer require mautic/helloworld-bundle -``` - -The `composer create-project` command passes ownership of all files to the -project that is created. You should create a new git repository, and commit -all files not excluded by the .gitignore file. - -## What does the template do? - -When installing the given `composer.json` some tasks are taken care of: - -* Mautic will be installed in the `docroot`-directory. -* Autoloader is implemented to use the generated composer autoloader in `vendor/autoload.php`, - instead of the one provided by Mautic (`docroot/vendor/autoload.php`). -* Plugins (packages of type `mautic-plugin`) will be placed in `docroot/plugins/` -* Themes (packages of type `mautic-theme`) will be placed in `docroot/themes/` -* Creates `docroot/media`-directory. -* Creates environment variables based on your .env file. See [.env.example](.env.example). - -## Updating Mautic Core - -This project will attempt to keep all of your Mautic Core files up-to-date; the -project [mautic/core-composer-scaffold](https://github.com/mautic/core-composer-scaffold) -is used to ensure that your scaffold files are updated every time mautic/core is -updated. If you customize any of the "scaffolding" files (commonly .htaccess), -you may need to merge conflicts if any of your modified files are updated in a -new release of Mautic core. - -Follow the steps below to update your core files. - -1. Run `composer update mautic/core --with-dependencies` to update Mautic Core and its dependencies. -2. Run `git diff` to determine if any of the scaffolding files have changed. - Review the files for any changes and restore any customizations to - `.htaccess` or others. -1. Commit everything all together in a single commit, so `docroot` will remain in - sync with the `core` when checking out branches or running `git bisect`. -1. In the event that there are non-trivial conflicts in step 2, you may wish - to perform these steps on a branch, and use `git merge` to combine the - updated core files with your customized files. This facilitates the use - of a [three-way merge tool such as kdiff3](http://www.gitshah.com/2010/12/how-to-setup-kdiff-as-diff-tool-for-git.html). This setup is not necessary if your changes are simple; - keeping all of your modifications at the beginning or end of the file is a - good strategy to keep merges easy. - -## FAQ - -### Should I commit the contributed plugins I download? - -Composer recommends **no**. They provide [argumentation against but also -workrounds if a project decides to do it anyway](https://getcomposer.org/doc/faqs/should-i-commit-the-dependencies-in-my-vendor-directory.md). - -### Should I commit the scaffolding files? - -The [Mautic Composer Scaffold](https://github.com/mautic/core-composer-scaffold) plugin can download the scaffold files (like -index.php, .htaccess, …) to the docroot/ directory of your project. If you have not customized those files you could choose -to not check them into your version control system (e.g. git). If that is the case for your project it might be -convenient to automatically run the mautic-scaffold plugin after every install or update of your project. You can -achieve that by registering `@composer mautic:scaffold` as post-install and post-update command in your composer.json: - -```json -"scripts": { - "post-install-cmd": [ - "@composer mautic:scaffold", - "..." - ], - "post-update-cmd": [ - "@composer mautic:scaffold", - "..." - ] -}, -``` -### How can I apply patches to downloaded plugins? - -If you need to apply patches (depending on the project being modified, a pull -request is often a better solution), you can do so with the -[composer-patches](https://github.com/cweagans/composer-patches) plugin. - -To add a patch to Mautic plugin foobar insert the patches section in the extra -section of composer.json: -```json -"extra": { - "patches": { - "mautic/foobar": { - "Patch description": "URL or local path to patch" - } - } -} -``` - -### How do I specify a PHP version ? - -This project supports PHP 7.4 as minimum version, however it's possible that a `composer update` will upgrade some package that will then require PHP 7+ or 8+. - -To prevent this you can add this code to specify the PHP version you want to use in the `config` section of `composer.json`: -```json -"config": { - "sort-packages": true, - "platform": { - "php": "7.4" - } -}, -``` - -### How do I use another folder than docroot as webroot - -By default the composer.json file is configures to put all Mautic core, plugin and theme files in the `docroot` folder. -It is possible to change this folder to your own needs. - -In following examples, we will change `docroot` into `public`. - -##### New installations - -* Run the `create-project` command without installing - ```bash - composer create-project mautic/recommended-project:^4.0 some-dir --no-interaction --no-install - ``` -* Do a find and replace in the `composer.json` file to change `docroot/` into `public/`. -* Review the changes in the `composer.json` file to ensure there are no unintentional replacements. -* Run `composer install` to install all dependencies in the correct location. - -##### Existing installations - -* move the `docroot/` to `public/` - ```bash - mv docroot public - ``` -* Do a find and replace in the `composer.json` file to change `docroot/` into `public/`. -* review the changes in the `composer.json` file to ensure there are no unintentional replacements. -* run `composer update --lock` to ensure the autoloader is aware of the changed folder. diff --git a/docs/some-dir/composer.json b/docs/some-dir/composer.json deleted file mode 100644 index d8efdcd0..00000000 --- a/docs/some-dir/composer.json +++ /dev/null @@ -1,168 +0,0 @@ -{ - "name": "mautic/recommended-project", - "description": "Project template for Mautic 5 projects with composer", - "type": "project", - "license": "GPL-2.0-or-later", - "homepage": "https://www.mautic.org/mautic-releases", - "support": { - "user-docs": "https://docs.mautic.org/en", - "developer-docs": "https://developer.mautic.org", - "chat": "https://www.mautic.org/slack" - }, - "funding": [ - { - "type": "other", - "url": "https://opencollective.com/mautic" - }, - { - "type": "other", - "url": "https://github.com/sponsors/mautic" - } - ], - "authors": [ - { - "name": "", - "role": "" - } - ], - "require": { - "composer/installers": "^1.11", - "mautic/core-composer-scaffold": "4.x-dev", - "mautic/core-project-message": "4.x-dev", - "mautic/core-lib": "5.2.5", - "mautic/grapes-js-builder-bundle": "5.2.5", - "mautic/plugin-clearbit": "5.2.5", - "mautic/plugin-cloudstorage": "5.2.5", - "mautic/plugin-crm": "5.2.5", - "mautic/plugin-emailmarketing": "5.2.5", - "mautic/plugin-focus": "5.2.5", - "mautic/plugin-fullcontact": "5.2.5", - "mautic/plugin-gmail": "5.2.5", - "mautic/plugin-outlook": "5.2.5", - "mautic/plugin-social": "5.2.5", - "mautic/plugin-tagmanager": "5.2.5", - "mautic/plugin-zapier": "5.2.5", - "mautic/theme-aurora": "5.2.5", - "mautic/theme-blank": "5.2.5", - "mautic/theme-brienz": "5.2.5", - "mautic/theme-cards": "5.2.5", - "mautic/theme-confirmme": "5.2.5", - "mautic/theme-fresh-center": "5.2.5", - "mautic/theme-fresh-fixed": "5.2.5", - "mautic/theme-fresh-left": "5.2.5", - "mautic/theme-fresh-wide": "5.2.5", - "mautic/theme-goldstar": "5.2.5", - "mautic/theme-neopolitan": "5.2.5", - "mautic/theme-oxygen": "5.2.5", - "mautic/theme-paprika": "5.2.5", - "mautic/theme-skyline": "5.2.5", - "mautic/theme-sparse": "5.2.5", - "mautic/theme-sunday": "5.2.5", - "mautic/theme-vibrant": "5.2.5", - "mautic/theme-trulypersonal": "5.2.5", - "mautic/theme-1-2-1-2-column": "5.2.5", - "mautic/theme-1-2-1-column": "5.2.5", - "mautic/theme-1-2-column": "5.2.5", - "mautic/theme-1-3-1-3-column": "5.2.5", - "mautic/theme-1-3-column": "5.2.5", - "mautic/theme-attract": "5.2.5", - "mautic/theme-connect-through-content": "5.2.5", - "mautic/theme-creative": "5.2.5", - "mautic/theme-educate": "5.2.5", - "mautic/theme-gallery": "5.2.5", - "mautic/theme-make-announcement": "5.2.5", - "mautic/theme-showcase": "5.2.5", - "mautic/theme-simple-text": "5.2.5", - "mautic/theme-survey": "5.2.5", - "mautic/theme-welcome": "5.2.5" - }, - "repositories": [ - { - "type": "git", - "url": "https://github.com/mautic/FOSOAuthServerBundle.git" - }, - { - "type": "git", - "url": "https://github.com/mautic/SpBundle.git" - }, - { - "type": "git", - "url": "https://github.com/mautic/SymfonyBridgeBundle.git" - } - ], - "conflict": { - "mautic/mautic": "*", - "mautic/core": "*" - }, - "minimum-stability": "dev", - "prefer-stable": true, - "config": { - "sort-packages": true, - "allow-plugins": { - "composer/installers": true, - "symfony/flex": true, - "mautic/core-composer-scaffold": true, - "mautic/core-project-message": true, - "php-http/discovery": true - } - }, - "autoload": { - "psr-4": { - "MauticPlugin\\": "docroot/plugins/" - } - }, - "scripts": { - "post-install-cmd": [ - "@npm-ci", - "@npx-patch-package", - "@generate-assets" - ], - "post-update-cmd": [ - "@npm-ci", - "@npx-patch-package", - "@generate-assets" - ], - "npm-ci": "npm ci --prefer-offline --no-audit", - "npx-patch-package": "npx patch-package", - "generate-assets": "@php bin/console mautic:assets:generate" - }, - "extra": { - "mautic-scaffold": { - "locations": { - "web-root": "docroot/" - } - }, - "installer-paths": { - "docroot/app": [ - "type:mautic-core" - ], - "docroot/plugins/{$name}": [ - "type:mautic-plugin" - ], - "docroot/themes/{$name}": [ - "type:mautic-theme" - ] - }, - "mautic-core-project-message": { - "include-keys": [ - "homepage", - "support" - ], - "post-create-project-cmd-message": [ - "<bg=blue;fg=white> </>", - "<bg=blue;fg=white> Congratulations, you’ve installed the Mautic codebase </>", - "<bg=blue;fg=white> from the mautic/recommended-project template! </>", - "<bg=blue;fg=white> </>", - "", - "<bg=yellow;fg=black>Next steps</>:", - " * Install Mautic", - " * Read the user guide", - " * Get support: https://www.mautic.org/support", - " * Get involved with the Mautic community:", - " https://www.mautic.org/getting-involved", - " * Remove the plugin that prints this message:", - " composer remove mautic/core-project-message" - ] - } - } -} From 57b6f71ce11e4e6f5327a7ad10a11b2a0d927f60 Mon Sep 17 00:00:00 2001 From: David Jarvis aka DJ <dcjarvis@yahoo.com> Date: Tue, 20 May 2025 11:59:20 +0100 Subject: [PATCH 14/28] updates from prose review 2 --- docs/campaigns/importing_campaigns.rst | 36 +++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/campaigns/importing_campaigns.rst b/docs/campaigns/importing_campaigns.rst index 75b86380..669966ac 100644 --- a/docs/campaigns/importing_campaigns.rst +++ b/docs/campaigns/importing_campaigns.rst @@ -7,7 +7,7 @@ Importing Campaigns The Import feature allows you to add pre-configured Campaigns to your Mautic instance using zip files containing all the relevant data to construct a Campaign. -Import Process +Import process -------------- Step-by-step import @@ -17,7 +17,7 @@ Step-by-step import Navigate to the main dashboard and authenticate. 2. **Access Campaigns Section** - Click on the Campaigns menu to view existing campaigns. + Click on the Campaigns menu to view existing Campaigns. 3. **Open Import Options** - Click the cog icon in the top-right corner @@ -27,31 +27,31 @@ Step-by-step import On the import screen: - Choose the Campaign zip file you wish to import - - **Recommended:** Use a ZIP file created from the Mautic export function - - Ensures inclusion of Campaign data, external assets, and dynamic content + - **Recommended:** use a ZIP file created from the Mautic export function + - Ensures inclusion of Campaign data, external Assets, and Dynamic Content .. important:: - Only ZIP files are supported for Campaign imports using the UI. In addition, correctly structured JSON files can be used with both the command line and API endpoints. + The UI only supports importing ZIP files. You can use both the command line and API endpoints to import correctly structured JSON files. Import mechanics **************** -During the import process, Mautic performs a comprehensive analysis of the data to be imported: +During the import process, Mautic performs a comprehensive analysis of the data to import: -- Checks that the logged in user has the correct permissions to be able to import -- Identifies required entities for campaign functionality -- Validates if a campaign template depends on an external plugin: +- Checks that the logged in User has the correct permissions to import +- Identifies required entities for Campaign functionality +- Validates if a Campaign template depends on an external Plugin: - * The import function verifies plugin installation - * When a required plugin is missing: + * The import function verifies Plugin installation + * When a required Plugin is missing: - The import process halts - - Prompts the user to install the necessary plugin before continuing + - Prompts the User to install the necessary Plugin before continuing - Validates for potential ID conflicts in imported entities - Where conflicts exist, provides options to: - * Update existing entities (allowing administrators to update existing campaigns) + * Update existing entities (allowing Administrators to update existing Campaigns) * Create new entities, using a new ID - **Automatic Data Mapping** @@ -60,16 +60,16 @@ During the import process, Mautic performs a comprehensive analysis of the data - **Campaign Activation** * After successful import, the campaign remains inactive by default, so that you stay in control - * Navigate to the campaign list to activate imported campaigns + * Navigate to the Campaigns list to activate imported Campaigns .. tip:: -To activate the imported campaign: +To activate the imported Campaign: 1. Go to the Campaigns section - 2. Locate the newly imported campaign using the toggle next to the campaign name |activate_toggle|. - 3. Toggle the campaign status to "Active" + 2. Locate the newly imported Campaign using the toggle next to the Campaign name |activate_toggle|. + 3. Toggle the Campaign status to "Active" .. |activate_toggle| image:: images/activate-campaign.png - :alt: Activate campaign toggle + :alt: Activate Campaign toggle :height: 20px Importing via the command line From 1ff47f685fe66ae1e2aeb86bfd4834f0737b2070 Mon Sep 17 00:00:00 2001 From: David Jarvis aka DJ <dcjarvis@yahoo.com> Date: Thu, 22 May 2025 12:31:34 +0100 Subject: [PATCH 15/28] Resolve .gitignore merge conflict --- .DS_Store | Bin 8196 -> 0 bytes .gitignore | 16 ++++++++++++++++ docs/.DS_Store | Bin 6148 -> 0 bytes docs/campaigns/.DS_Store | Bin 8196 -> 0 bytes 4 files changed, 16 insertions(+) delete mode 100644 .DS_Store delete mode 100644 docs/.DS_Store delete mode 100644 docs/campaigns/.DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 2fc29c834656c1b3964a9bdf9b62442724b37d06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8196 zcmeHMTWl0n7(U;$&>1?=0gAM^6BY_V$O0|5mYdn$lzV|~>9$;!*`1LNOlQi@?6y!E z8xvpTVtmqg=SicH2Ls+-d{7a6R5ZaDABY<D1rvQydGMb(v!oZA_+X6UoaCH;{{Q^{ zIrIPL`_I`k%NRpTL2qEJhA}2m^{G-x&0UJ$@7GC12qoo2LG~;==niBpCrkXhle8m2 z6oDuLQ3Rq0L=lK0@Nb9!o!Nd7r#Sa{Z`4K+h$8U6jDWvC#Hso;8PZWsgRO&_;0i$0 zzXAk>o)!OrXgs9JkdAUnJygI=iE>kfR}64-lE*{6WJpIj<>n0G<pbf&2yZ9|W~cGv zfjUEy)2NLi5Jg~e1pM?VXF79Oj=5v+@7V#%biMleFCmnbPn%v*AyrgX98UE)gDFq* z3T}&*Kj?Fvj$>s@&sz3?X$)qR>Q=}0G}AWnfq|(Tq;ja+v~_2&#VOdj8`!3Jha`zo zMyVYhZfIK9kch8byI~{|A8y*XJ`r!+&@eJ0iSz1KZ{D9dVfEY2S?&YEeSpoJ;M2LW z4q0xom4A>ZOO+h@Ds)JcW>m^E)$Vjp??E}E%qh~FcMN!@W4i~8yh}8rRJr8g_MBty z%^QAych0d2{kEG?X6H03SFkkCXtu1BdD<XZm7B{umbKS$O@El4v}X)?e&3|7bg%2= zz1Co`v`OQ0Ev4WZ`wbe@EE;Vzv|ByFIY^gg&0o0Wft70-w<O!Q@7{O4N|`-pZnZ4; zQYdZDe8e>JU4y3X_4R0O&aidUKGt0{T{CMLown9*%tk_zu2<F6F1SCYs#B?^3t97I z!Sqfkb)p!|5nrUrhje<U-Hz_hqAOQK8i!_Ix=2+I`aP|AgIYZ_SiVA)J6&>K(L%*l zt7W<K1YtmN{Z}B<wd-WH%hIxjrRWl$r)i_Cb{Bd4m7Sq*YqhMVb54Ihx#)(n<`z{w z;!nFvbG?oM!=_id7S8<;_4T@aTHfdwBn*bq9V88wl;plqyKZ_;&`HtQLtgaw!!_%A z;KPEN+A1}rc8SVoVn$I+yJ!;bX<0e^#k%By(O`Azk}LQs)s+@22y|Vtp7Ro&Y${vB z+So33l;znlJI|hB7ujX@5xdI1VBfGG*)8@Pfa#Dh1C^-80xUrS>(PiNY(xt>un+r@ z!Xfm*#BsP7!f6cS3?9c5IEVANfT!^SUc$?`h}ZBYF5?Q`!uz;}Pw*)|$4z{T@9;ft z;dlIj+rl(qzEC673X6q=uu@nhGzv|^CSkL%N9YjJf+idn9ASt%Ae2(=aLK_?ev0Uw zy@OIM{PrhYx_hd{9^STn$Ikyws@<5ylWX36^W#fbtX|i&wfPPRIciHl9Si>e^W!ol zz<hv-33$EEvrt}G%Fv1F*hR@=42gcuDivINvAjg2WKpJZcA30fq%={cb2brMB~oH4 zLcq!mv9%JVpHjxzM66K~6>w&gyhWl!RLVK4%1KF5Sir<uW9=d(lOq0ID8IpOvLD#b z>^7l%7UrS`%TQ0)eh}NRhtTa~y9-B<frcz}^uxvgP7=<~U<8k16p!I7;rs%g#8Y?{ z&*6Ezh*$6`Vf}TyK}dfa@8CUrfDdsMABUL#nh^gJewl#c-Q!V=U&o@jn9AFhV;>_~ z3H9UE?<KB`J5%G||J$ej{(mpI9QhJOAd0|*2%x+z)z(77skS`-t{ta(l&W8Ry-`jB t6KckN2q6B>ABOZCCsp4j8PZWsNkZix{}Aw(e`-hPe{}u_5WOdxKLPphXutpf diff --git a/.gitignore b/.gitignore index 4f074fd0..b0eb7888 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,18 @@ .DS_Store **/.DS_Store +# Created by https://www.toptal.com/developers/gitignore/api/python,jupyternotebooks +# Edit at https://www.toptal.com/developers/gitignore?templates=python,jupyternotebooks + +### JupyterNotebooks ### +# gitignore template for Jupyter Notebooks +# website: http://jupyter.org/ + +.ipynb_checkpoints +*/.ipynb_checkpoints/* + +# IPython +profile_default/ +ipython_config.py + +### Python ### + diff --git a/docs/.DS_Store b/docs/.DS_Store deleted file mode 100644 index 170d3d735206dadf64d8bc1a1cffeeddd2e12359..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKO;6iE5S@iU?4n9Jv=Y6o#5Jh6A&`iRmC`>@BRJ41wd;slHQp#rnnM)HXW$35 zf26;sZ*~_cB*38uTD3dU&YS(1wccCpu9t{JZ;}Q?4I=VT7;Deq?lJD?vSMqtWfLg$ z9BDEwhe@uIm9ehDDc}_N`xM~4yFm#}D5IPf_wV;)m}lj9Z||pATd!?w*6V(KtNt<i zqNdR}9#6_%JUZvviBfrT8^_6MHkkGsFW#zR9B0K~q!Y4qfFU2wvLaQ}o|+VCsdFRK z;rpK7Z*0$ILATRx$~UcfQ_ebFgog*geC~TMcU~X8?|;oN3w32g6gY9TJ-2uZ*9aCE zeTzm#u8Iq^a)wWzDW*#r!e?|r1(geD)*s=#f7oiupwl&+5x2YT-{5=?vnB2xm88IZ zK)aNJ{b+&tk0)R4S6J=Ne$bS=t%FCFo0k*z;Bw!fbA{LvaXo``%L>>X<ZUf?xVlro zDe&Jbz~_TRVGJ!+2KCVar=I}88k)5s=C=grNQ<Gx${>1R!lVLCs<2NCVbakq?YPil zWzeLPu#XR6zbxzvMd+_%eyN9(2n{;#6mSaMRbbsz8+`x2d-(l-H_6qU0#1Q{N&!(j zj*fevWbf7$a(vfHlusyZJgzdRQ{e1%tOLG^w^6jAPjUbZEmj860&_nCS_bEw0#B;I EFW>6QBme*a diff --git a/docs/campaigns/.DS_Store b/docs/campaigns/.DS_Store deleted file mode 100644 index 0a8295168b8722aa9e565b22d991a52ba1538bbe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8196 zcmeHMU2GIp6u#fI(3vTfDYj5{1U57RmaVkl0xcM}f5?vrvMntITxWNNGGRJXc4l`$ zLTog?7=99s@k#uDl*AZ)Fc85PiEo;!@M0n+zUYH7>XQkcJ9kRxLLYcB80IE(&zyVD znS1YdzcY8wEMpA4C9{*U7RH!N7nfQ+Rd*@8pI>(rDQc-D3G!#mVt&5jPvjjhe}{G4 z5F-#H5F-#H5F-#Ha6ceGXErbLEa$%Hjq4bJ7=Z^80sekS(#2&al9NJ)uMVoh6o4d8 z0m4G}lm~=9$w+1*IVoi5O3xJ810q*MDF%c)^(VPG$xI|Cg%s`#!kr-+8BvCUaCGuZ zhUN?zA>%qmAV%O$1bFz&Wi}gU1{<r&-!JAT9NP~%I=+WgQ#)sFU7b=_Uw0^b#H(Zj zBPjX3M)3gG20hQoPv0B)eYRD}X-$2e8yL206+;Kxw8-S(u<e>&rPnLDrXTudMTeru zN=|#^<jKyijp<afJ)KUUN+nNrZs^*WN_MVW*LCWYA~(0L*|axz)ERfZvtlWTUI(n9 zYH8=H*X8)-KJi_mCNaIPYtg!7MXlF|NA{~ZZ9$nPzk4FEJ=fo76@8L9ZN5*7++Oh9 zJw=Q64;MVAH17I2t+8M@g_2_gR*&Ok?H4S6B(RPL{BG3uia}p!IAW78Z_8;~$+z}e z<Y1Sresc4hswNKQ!o21s%U7)KNOy1P-@fbFsfLH<YmEz3b(D~D1N*RT6^AOe864Sf z_yx-~ZFg+AZ2NZJu?AgZ+-j`-Qc<QF7A|U8oY3{zOh-z2`&h{iPH3&N9FDPcnXVo* zX->O5laFF*pUU(c8ohOyt{>n%eMO6EGjdqDN>>McT9#%+hN*T{9Xv`~toeKj<l4Db z)rTA-Z#kN&h;i0$Q1#)mz*gHCHE!Ch>LUeje4Li(HzE}^XL@w~IX?2!h9B&nuw0te zm1y{Zt-3zyA2Ev7?h3I?yBf9cp!RUjsE4}^n0DY5c_%^g3@v3pe_)Skh6`y~xTrzT z>O->jtxVfvyJefWP1`OgF1Kdd#14hnwq`nnrqFq{m_1IX(>8XH;%0)KV5iwz>^%F3 zea5b^uh=*2I{T6R#BQ*k*>CI*_77^&fcenShy+&RF|=Vdo<IgY=*3R#!CquBh(pN3 zgoVTKP(lUAaRM*lRh-5doW+}X2k+wpe25FUgfDOzSMW8y!w<NI>$rhG@fZHaO-Yed zsa|T5nx#cji?muwNolED>JyWfrlaP-O!7(nq|bO8P}D?U^Q2>Ui<-pNr?%Z6QFDEs zh?9q#nwPe&YG1p4Q|1<*1OcZxcRQ0q`pzJINZ)X;t9Umh0!m#n9c<N+HAK9(j?0|9 zTAi>?s>@~Ky*5XvkE$zWV!Sq2sBMXqOfjZOp{lM)bSe}{T8&WK5@|)&Ae4=2w?Z+c z)e7}Vb&JBoTw|f0=uPy?6c?I&?{xi`ea<ekt89ws`V0G&{myPeCXy~h5-W(NoTTko zj}7QTH#TDcTd@t>iLiqh!Z1cKiUJY#Fveh`2p<8;coCCC+L!SPQT7~O!|Qkh=kYe) z#d{HXKf%Q+GVYj3MqH{UV>w%N9nT%3_GtpobcwZlLZC)ixA6RbVD|6-_w>xzI7T2w z;QxgHYWuVOy)@_9-l{y;PSW)}UA%C+Ng+cQs={=fWSEYVy!D46-6zSE`(z?HDI~Q} X`OiNDxbgXa`_I+*{10jPz*YYOdFLrv From 0ad90a0e250eb17b307cf8f40fc594d4ff50d998 Mon Sep 17 00:00:00 2001 From: David Jarvis aka DJ <dcjarvis@yahoo.com> Date: Tue, 20 May 2025 12:12:47 +0100 Subject: [PATCH 16/28] prose review 3 --- docs/campaigns/importing_campaigns.rst | 26 +++++++++---------- docs/configuration/command_line_interface.rst | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/campaigns/importing_campaigns.rst b/docs/campaigns/importing_campaigns.rst index 669966ac..edb246fb 100644 --- a/docs/campaigns/importing_campaigns.rst +++ b/docs/campaigns/importing_campaigns.rst @@ -39,7 +39,7 @@ Import mechanics During the import process, Mautic performs a comprehensive analysis of the data to import: - Checks that the logged in User has the correct permissions to import -- Identifies required entities for Campaign functionality +- Identifies required entities for the Campaign to function - Validates if a Campaign template depends on an external Plugin: * The import function verifies Plugin installation @@ -51,7 +51,7 @@ During the import process, Mautic performs a comprehensive analysis of the data - Validates for potential ID conflicts in imported entities - Where conflicts exist, provides options to: - * Update existing entities (allowing Administrators to update existing Campaigns) + * Update existing entities, allowing Administrators to update existing Campaigns * Create new entities, using a new ID - **Automatic Data Mapping** @@ -59,7 +59,7 @@ During the import process, Mautic performs a comprehensive analysis of the data * Creates any necessary dependent entities automatically - **Campaign Activation** - * After successful import, the campaign remains inactive by default, so that you stay in control + * After successful import, the Campaign remains inactive by default, so that you stay in control * Navigate to the Campaigns list to activate imported Campaigns .. tip:: To activate the imported Campaign: @@ -75,7 +75,7 @@ To activate the imported Campaign: Importing via the command line ------------------------------ -You can import campaigns using the Mautic command-line console: +You can import Campaigns using the Mautic command-line console: .. code-block:: bash @@ -84,15 +84,15 @@ You can import campaigns using the Mautic command-line console: --file=/tmp/entity_data.zip \ --user=<user_id> -Command Parameters +Command parameters ****************** - ``--entity=campaign`` * Specifies the type of entity being imported - * In this case, importing a campaign + * In this case, importing a Campaign - ``--file=/tmp/entity_data.zip`` - * Path to the ZIP file containing the campaign data + * Path to the ZIP file containing the Campaign data * Must be a valid export file created from a Mautic export - ``--user=<user_id>`` @@ -100,21 +100,21 @@ Command Parameters * Ensures proper access and permissions for the import process .. important:: - - Ensure the ZIP file is a valid Mautic campaign export - - The specified user must have appropriate import permissions + - Ensure the ZIP file is a valid Mautic Campaign export + - The specified User must have appropriate import permissions - Verify the file path is correct before running the command Importing using the Mautic API ------------------------------ -You can import campaigns programmatically using the Mautic API: +You can Import Campaigns programmatically using the Mautic API: Curl Example (ZIP File) *********************** .. code-block:: bash - curl -X POST 'https://{your-mautic-domain}/api/campaigns/import' \ + curl -X POST 'https://{your-domain}/api/campaigns/import' \ -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \ -H 'Content-Type: multipart/form-data' \ -F 'file=@/path/to/campaign_export.zip' @@ -127,7 +127,7 @@ Python Example (JSON Data) import requests # API Endpoint - url = 'https://{your-mautic-domain}/api/campaigns/import' + url = 'https://{your-domain}/api/campaigns/import' # Authentication headers = { @@ -166,7 +166,7 @@ Mautic supports two primary methods of API-based campaign import: - Useful for creating new campaigns or updating existing ones .. important:: - - Replace ``{your-mautic-domain}`` with your actual Mautic instance domain + - Replace ``{your-domain}`` with your actual Mautic instance domain - Ensure you have a valid access token by accessing the API Credentials section within Mautic's settings. - The imported campaign must comply with Mautic's campaign structure - Verify import permissions and data integrity diff --git a/docs/configuration/command_line_interface.rst b/docs/configuration/command_line_interface.rst index be435f3c..5dba69d4 100644 --- a/docs/configuration/command_line_interface.rst +++ b/docs/configuration/command_line_interface.rst @@ -126,7 +126,7 @@ These are the commands you may need to use in relation to your Mautic instance. - Analyze Custom Fields table and return table or file with results. See :doc:`/contacts/custom_fields`. - * - ``mautic:import`` - - Imports contacts from a CSV file + - Imports Contacts from a CSV file - If the CSV import is configured to run in background then this command will pick up the pending import jobs and imports the data from CSV files to Mautic. - * - ``mautic:integration:fetchleads`` From 1fc3e866fead3ce6c8b2aea0538bc4a28b1dd909 Mon Sep 17 00:00:00 2001 From: David Jarvis aka DJ <dcjarvis@yahoo.com> Date: Tue, 20 May 2025 12:21:12 +0100 Subject: [PATCH 17/28] prose review 4 --- docs/campaigns/importing_campaigns.rst | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/campaigns/importing_campaigns.rst b/docs/campaigns/importing_campaigns.rst index edb246fb..fbf3c609 100644 --- a/docs/campaigns/importing_campaigns.rst +++ b/docs/campaigns/importing_campaigns.rst @@ -102,15 +102,15 @@ Command parameters .. important:: - Ensure the ZIP file is a valid Mautic Campaign export - The specified User must have appropriate import permissions - - Verify the file path is correct before running the command + - Verify the path is correct before running the command Importing using the Mautic API ------------------------------ You can Import Campaigns programmatically using the Mautic API: -Curl Example (ZIP File) -*********************** +Curl example with ZIP file +************************** .. code-block:: bash @@ -119,7 +119,7 @@ Curl Example (ZIP File) -H 'Content-Type: multipart/form-data' \ -F 'file=@/path/to/campaign_export.zip' -Python Example (JSON Data) +Python example with JSON data ************************** .. code-block:: python @@ -150,23 +150,23 @@ Python Example (JSON Data) imported_campaign = response.json() print("Campaign imported successfully") -API Import Methods +API import methods ****************** -Mautic supports two primary methods of API-based campaign import: +Mautic supports two primary methods of API-based Campaign import: 1. **ZIP File Import** - Use ``multipart/form-data`` content type - - Upload the complete campaign export ZIP file - - Includes all campaign assets and dependencies from the ZIP file + - Upload the complete Campaign export ZIP file + - Includes all Campaign assets and dependencies from the ZIP file 2. **JSON Data Import** - Use ``application/json`` content type - - Send campaign details directly in the request body - - Useful for creating new campaigns or updating existing ones + - Send Campaign details directly in the request body + - Useful for creating new Campaigns or updating existing Campaigns .. important:: - Replace ``{your-domain}`` with your actual Mautic instance domain - Ensure you have a valid access token by accessing the API Credentials section within Mautic's settings. - - The imported campaign must comply with Mautic's campaign structure + - The imported Campaign must comply with Mautic's Campaign structure - Verify import permissions and data integrity From ef57099139a97443ece610f542796e3b2fd5be5d Mon Sep 17 00:00:00 2001 From: David Jarvis aka DJ <dcjarvis@yahoo.com> Date: Tue, 20 May 2025 12:40:38 +0100 Subject: [PATCH 18/28] prose review 5 --- docs/campaigns/exporting_campaigns.rst | 40 +++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/campaigns/exporting_campaigns.rst b/docs/campaigns/exporting_campaigns.rst index 8e33429e..15674fa7 100644 --- a/docs/campaigns/exporting_campaigns.rst +++ b/docs/campaigns/exporting_campaigns.rst @@ -10,50 +10,50 @@ Before importing or exporting data, as a safety precaution take a backup of your .. important:: Both the import and the export features are only available in Mautic 7.0 and later versions. -Supported Data Types +Supported data types -------------------- -Once a campaign is selected, the export feature will extract all campaign data and entities and any dependencies that the campaign needs to function. This includes: +Once a Campaign is selected, the export feature will extract all Campaign data and entities and any dependencies that the Campaign needs to function. This includes: - - Dynamic content + - Dynamic Content - Assets - Custom Fields - Other related dependencies The export command will: - - Detect use of plugins and Custom Fields + - Detect use of Plugins and Custom Fields - Include the data to support these dependencies .. important:: - The importing instance will need the same Custom Fields and plugins to be present. + The importing instance will need the same Custom Fields and Plugins to be present. -Export Mechanic +Export mechanic *************** -Whether exporting via the UI, the command line or using the API, the export feature +Whether exporting via the UI, the command line or using the API, the Export feature follows the same process. - Checks that the user has adequate permissions to export - - Supports exporting multiple campaigns simultaneously + - Supports exporting multiple Campaigns simultaneously - Exports data in a structured JSON format - Exports assets into a separate folder in their original format - Zips the resulting collection of files for easy transfer across systems -Export Methods +Export methods ************** The export feature can be used in three ways: -**1. UI-Based Export** +**1. UI-based export** ---------------------- Manual export through Mautic Campaigns dashboard: 1. Go to the Campaigns menu -2. Select the campaign you want to export -3. Select the export option from the dropdown menu located next to the item selection +2. Select the Campaign you want to Export +3. Select the Export option from the dropdown menu located next to the item selection -**2. CLI-Based Export** +**2. CLI-based export** ----------------------- Use the following commands: @@ -62,9 +62,9 @@ Use the following commands: bin/console mautic:entity:export --entity=campaign --id=1 --zip-file -* `entity` defines the type of entity to export, in this case `campaign` -* `id` defines the id of the campaign to export. Look at the URL to find the ID when you view or edit the campaign - the ID will appear in the URL for example, /s/campaigns/view/123 where 123 is the ID -* `zip-file` creates a zip file of the campaign and its dependencies +* `entity` defines the type of entity to Export, in this case `campaign` +* `id` defines the id of the Campaign to Export. Look at the URL to find the ID when you view or edit the Campaign - the ID will appear in the URL for example, /s/campaigns/view/123 where 123 is the ID +* `zip-file` creates a zip file of the Campaign and its dependencies .. code-block:: bash @@ -72,12 +72,12 @@ Use the following commands: * Creates only a JSON file and ignores any additional assets -**3. API-Based Export** +**3. API-based export** ----------------------- -You can export campaigns programmatically using the Mautic API. You will need to authenticate for the API request. using the API credentials stored in Mautic's settings. For more detail on how to authenticate, see the `Mautic API documentation <https://docs.mautic.org/en/5.x/authentication/authentication.html>`_. +You can export Campaigns programmatically using the Mautic API. You will need to authenticate for the API request. using the API credentials stored in Mautic's settings. For more detail on how to authenticate, see the `Mautic API documentation <https://docs.mautic.org/en/5.x/authentication/authentication.html>`_. -Curl Example +Curl example ************ .. code-block:: bash @@ -86,7 +86,7 @@ Curl Example --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \ --data '' -Python Example +Python example ************** .. code-block:: python From 2b81a5f5790cd4028a88359827c5306008b4808c Mon Sep 17 00:00:00 2001 From: David Jarvis aka DJ <dcjarvis@yahoo.com> Date: Tue, 20 May 2025 12:55:38 +0100 Subject: [PATCH 19/28] prose review 6 --- docs/campaigns/exporting_campaigns.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/campaigns/exporting_campaigns.rst b/docs/campaigns/exporting_campaigns.rst index 15674fa7..64f056ea 100644 --- a/docs/campaigns/exporting_campaigns.rst +++ b/docs/campaigns/exporting_campaigns.rst @@ -13,20 +13,20 @@ Before importing or exporting data, as a safety precaution take a backup of your Supported data types -------------------- -Once a Campaign is selected, the export feature will extract all Campaign data and entities and any dependencies that the Campaign needs to function. This includes: +When you select a Campaign, the export feature extracts all Campaign data and entities and any dependencies the Campaign needs to function. This includes: - Dynamic Content - Assets - Custom Fields - Other related dependencies -The export command will: +The export command: - Detect use of Plugins and Custom Fields - Include the data to support these dependencies .. important:: - The importing instance will need the same Custom Fields and Plugins to be present. + The importing instance needs the same Custom Fields and Plugins to be present. Export mechanic *************** @@ -36,13 +36,13 @@ follows the same process. - Checks that the user has adequate permissions to export - Supports exporting multiple Campaigns simultaneously - Exports data in a structured JSON format - - Exports assets into a separate folder in their original format + - Exports Assets into a separate folder in their original format - Zips the resulting collection of files for easy transfer across systems Export methods ************** -The export feature can be used in three ways: +The Export feature can be used in three ways: **1. UI-based export** ---------------------- From a91177794ec65450a5c5f91363e9d562e84ebfd6 Mon Sep 17 00:00:00 2001 From: David Jarvis aka DJ <dcjarvis@yahoo.com> Date: Tue, 20 May 2025 13:00:44 +0100 Subject: [PATCH 20/28] prose review complete --- docs/campaigns/exporting_campaigns.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/campaigns/exporting_campaigns.rst b/docs/campaigns/exporting_campaigns.rst index 64f056ea..a85875b0 100644 --- a/docs/campaigns/exporting_campaigns.rst +++ b/docs/campaigns/exporting_campaigns.rst @@ -42,7 +42,7 @@ follows the same process. Export methods ************** -The Export feature can be used in three ways: +You can use the Export feature in three ways: **1. UI-based export** ---------------------- From 4f4b5297027ce05a93c0bddc39c925af7c2f5419 Mon Sep 17 00:00:00 2001 From: David Jarvis aka DJ <dcjarvis@yahoo.com> Date: Thu, 22 May 2025 11:45:44 +0100 Subject: [PATCH 21/28] addition of path to export file command line parameter --- docs/campaigns/exporting_campaigns.rst | 3 ++- docs/configuration/command_line_interface.rst | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/campaigns/exporting_campaigns.rst b/docs/campaigns/exporting_campaigns.rst index a85875b0..33c6d06a 100644 --- a/docs/campaigns/exporting_campaigns.rst +++ b/docs/campaigns/exporting_campaigns.rst @@ -60,11 +60,12 @@ Use the following commands: .. code-block:: bash - bin/console mautic:entity:export --entity=campaign --id=1 --zip-file + bin/console mautic:entity:export --entity=campaign --id=1 --zip-file --path=path/to-file * `entity` defines the type of entity to Export, in this case `campaign` * `id` defines the id of the Campaign to Export. Look at the URL to find the ID when you view or edit the Campaign - the ID will appear in the URL for example, /s/campaigns/view/123 where 123 is the ID * `zip-file` creates a zip file of the Campaign and its dependencies +* `path` specifies the directory where the exported file will be saved .. code-block:: bash diff --git a/docs/configuration/command_line_interface.rst b/docs/configuration/command_line_interface.rst index 5dba69d4..e885b425 100644 --- a/docs/configuration/command_line_interface.rst +++ b/docs/configuration/command_line_interface.rst @@ -116,7 +116,7 @@ These are the commands you may need to use in relation to your Mautic instance. * - ``mautic:entity:import --entity=campaign file=path-to-file/entity_data.zip`` - Imports campaign and dependent entities to Mautic from a ZIP file. See :doc:`/campaigns/importing-campaigns`. - * - ``mautic:entity:export --entity=campaign --id=1`` + * - ``mautic:entity:export --entity=campaign --id=1 path=path/to-file`` - Exports campaign and dependent entities from Mautic to a ZIP file. See :doc:`/campaigns/exporting-campaigns`. * - ``messenger:consume email`` From e29ebd9718584d92bfdc81b41f600ef38103ad98 Mon Sep 17 00:00:00 2001 From: Ruth Cheesley <ruth@ruthcheesley.co.uk> Date: Tue, 4 Nov 2025 14:19:30 +0000 Subject: [PATCH 22/28] Vale fixes --- docs/campaigns/exporting_campaigns.rst | 41 +++++++++++++------------- docs/campaigns/importing_campaigns.rst | 2 +- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/docs/campaigns/exporting_campaigns.rst b/docs/campaigns/exporting_campaigns.rst index 33c6d06a..fd7f3e69 100644 --- a/docs/campaigns/exporting_campaigns.rst +++ b/docs/campaigns/exporting_campaigns.rst @@ -1,7 +1,7 @@ .. vale off Exporting Campaigns -########################## +################### .. vale on @@ -28,12 +28,13 @@ The export command: .. important:: The importing instance needs the same Custom Fields and Plugins to be present. -Export mechanic -*************** +How it works +************ Whether exporting via the UI, the command line or using the API, the Export feature follows the same process. - - Checks that the user has adequate permissions to export + + - Checks that the User has adequate permissions to export - Supports exporting multiple Campaigns simultaneously - Exports data in a structured JSON format - Exports Assets into a separate folder in their original format @@ -44,17 +45,17 @@ Export methods You can use the Export feature in three ways: -**1. UI-based export** ----------------------- +UI-based export +--------------- Manual export through Mautic Campaigns dashboard: -1. Go to the Campaigns menu -2. Select the Campaign you want to Export -3. Select the Export option from the dropdown menu located next to the item selection +# Go to the Campaigns menu +# Select the Campaign you want to Export +# Select the Export option from the dropdown menu located next to the item selection -**2. CLI-based export** ------------------------ +CLI-based export +---------------- Use the following commands: @@ -63,20 +64,20 @@ Use the following commands: bin/console mautic:entity:export --entity=campaign --id=1 --zip-file --path=path/to-file * `entity` defines the type of entity to Export, in this case `campaign` -* `id` defines the id of the Campaign to Export. Look at the URL to find the ID when you view or edit the Campaign - the ID will appear in the URL for example, /s/campaigns/view/123 where 123 is the ID +* `id` defines the id of the Campaign to Export. Look at the URL to find the ID when you view or edit the Campaign - the ID appears in the URL for example, ``/s/campaigns/view/123`` where 123 is the ID * `zip-file` creates a zip file of the Campaign and its dependencies -* `path` specifies the directory where the exported file will be saved +* `path` specifies the directory to save the exported file. .. code-block:: bash bin/console mautic:entity:export --entity=campaign --id=1 --json-file -* Creates only a JSON file and ignores any additional assets +* Creates only a JSON file and ignores any additional resources -**3. API-based export** ------------------------ +API-based export +---------------- -You can export Campaigns programmatically using the Mautic API. You will need to authenticate for the API request. using the API credentials stored in Mautic's settings. For more detail on how to authenticate, see the `Mautic API documentation <https://docs.mautic.org/en/5.x/authentication/authentication.html>`_. +You can export Campaigns programmatically using the Mautic API. You need to authenticate for the API request. using the API credentials stored in Mautic's settings. For more detail on how to authenticate, see the :doc:`Mautic API documentation authentication/authentication.html`. Curl example ************ @@ -96,7 +97,7 @@ Python example # API Endpoint campaign_id = 1 - url = f'https://{your-domain}/api/campaigns/export/{campaign_id}' + url = f'https://example.com/api/campaigns/export/{campaign_id}' # Authentication headers = { @@ -112,7 +113,7 @@ Python example # Save or process the exported campaign .. important:: - - Replace `{your-domain}` with your actual Mautic instance domain + - Replace `example.com` with your actual Mautic instance domain - Replace `YOUR_ACCESS_TOKEN` with a valid authentication token - - The API uses a GET request to export a specific campaign by ID + - The API uses a GET request to export a specific Campaign by ID - Ensure you have the necessary API permissions diff --git a/docs/campaigns/importing_campaigns.rst b/docs/campaigns/importing_campaigns.rst index fbf3c609..4389537b 100644 --- a/docs/campaigns/importing_campaigns.rst +++ b/docs/campaigns/importing_campaigns.rst @@ -1,7 +1,7 @@ .. vale off Importing Campaigns -########################## +################### .. vale on From e05328d19bd4c7c689734a2d0c60b6b50fc17504 Mon Sep 17 00:00:00 2001 From: Ruth Cheesley <ruth@ruthcheesley.co.uk> Date: Tue, 4 Nov 2025 14:27:40 +0000 Subject: [PATCH 23/28] RST syntax fixes --- docs/campaigns/exporting_campaigns.rst | 8 ++++++-- docs/campaigns/importing_campaigns.rst | 7 ++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/campaigns/exporting_campaigns.rst b/docs/campaigns/exporting_campaigns.rst index fd7f3e69..c26e57ee 100644 --- a/docs/campaigns/exporting_campaigns.rst +++ b/docs/campaigns/exporting_campaigns.rst @@ -28,8 +28,12 @@ The export command: .. important:: The importing instance needs the same Custom Fields and Plugins to be present. -How it works -************ +.. vale off + +How exporting Campaigns works +***************************** + +.. vale on Whether exporting via the UI, the command line or using the API, the Export feature follows the same process. diff --git a/docs/campaigns/importing_campaigns.rst b/docs/campaigns/importing_campaigns.rst index 4389537b..ec2ba277 100644 --- a/docs/campaigns/importing_campaigns.rst +++ b/docs/campaigns/importing_campaigns.rst @@ -61,8 +61,9 @@ During the import process, Mautic performs a comprehensive analysis of the data - **Campaign Activation** * After successful import, the Campaign remains inactive by default, so that you stay in control * Navigate to the Campaigns list to activate imported Campaigns - .. tip:: -To activate the imported Campaign: + +.. tip:: + To activate the imported Campaign: 1. Go to the Campaigns section 2. Locate the newly imported Campaign using the toggle next to the Campaign name |activate_toggle|. @@ -120,7 +121,7 @@ Curl example with ZIP file -F 'file=@/path/to/campaign_export.zip' Python example with JSON data -************************** +***************************** .. code-block:: python From ac6fd7bce0df12feff7f5167b6f7477b238427aa Mon Sep 17 00:00:00 2001 From: Ruth Cheesley <ruth@ruthcheesley.co.uk> Date: Tue, 4 Nov 2025 14:29:44 +0000 Subject: [PATCH 24/28] Fix grammar --- docs/campaigns/importing_campaigns.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/campaigns/importing_campaigns.rst b/docs/campaigns/importing_campaigns.rst index ec2ba277..805aa4ef 100644 --- a/docs/campaigns/importing_campaigns.rst +++ b/docs/campaigns/importing_campaigns.rst @@ -31,7 +31,7 @@ Step-by-step import - Ensures inclusion of Campaign data, external Assets, and Dynamic Content .. important:: - The UI only supports importing ZIP files. You can use both the command line and API endpoints to import correctly structured JSON files. + The user interface only supports importing ZIP files. You can use both the command line and API endpoints to import correctly structured JSON files. Import mechanics **************** From f91027185ab7728aab7fda48311308ae146dded7 Mon Sep 17 00:00:00 2001 From: Ruth Cheesley <ruth@ruthcheesley.co.uk> Date: Tue, 4 Nov 2025 14:30:31 +0000 Subject: [PATCH 25/28] Remove image height restriction --- docs/campaigns/importing_campaigns.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/campaigns/importing_campaigns.rst b/docs/campaigns/importing_campaigns.rst index 805aa4ef..47d4920c 100644 --- a/docs/campaigns/importing_campaigns.rst +++ b/docs/campaigns/importing_campaigns.rst @@ -71,7 +71,6 @@ During the import process, Mautic performs a comprehensive analysis of the data .. |activate_toggle| image:: images/activate-campaign.png :alt: Activate Campaign toggle - :height: 20px Importing via the command line ------------------------------ From 89478c08728a0d5e1d1375605bed223e140bc4a4 Mon Sep 17 00:00:00 2001 From: Ruth Cheesley <ruth@ruthcheesley.co.uk> Date: Tue, 4 Nov 2025 14:31:14 +0000 Subject: [PATCH 26/28] Fix spacing in list --- docs/campaigns/importing_campaigns.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/campaigns/importing_campaigns.rst b/docs/campaigns/importing_campaigns.rst index 47d4920c..ff8fe91d 100644 --- a/docs/campaigns/importing_campaigns.rst +++ b/docs/campaigns/importing_campaigns.rst @@ -67,6 +67,7 @@ During the import process, Mautic performs a comprehensive analysis of the data 1. Go to the Campaigns section 2. Locate the newly imported Campaign using the toggle next to the Campaign name |activate_toggle|. + 3. Toggle the Campaign status to "Active" .. |activate_toggle| image:: images/activate-campaign.png From 51362b59ad5e660ae65cec2e25501556d4dc0750 Mon Sep 17 00:00:00 2001 From: Ruth Cheesley <ruth@ruthcheesley.co.uk> Date: Tue, 4 Nov 2025 14:32:42 +0000 Subject: [PATCH 27/28] use example.com in examples. --- docs/campaigns/importing_campaigns.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/campaigns/importing_campaigns.rst b/docs/campaigns/importing_campaigns.rst index ff8fe91d..75835fe8 100644 --- a/docs/campaigns/importing_campaigns.rst +++ b/docs/campaigns/importing_campaigns.rst @@ -67,7 +67,7 @@ During the import process, Mautic performs a comprehensive analysis of the data 1. Go to the Campaigns section 2. Locate the newly imported Campaign using the toggle next to the Campaign name |activate_toggle|. - + 3. Toggle the Campaign status to "Active" .. |activate_toggle| image:: images/activate-campaign.png @@ -115,7 +115,7 @@ Curl example with ZIP file .. code-block:: bash - curl -X POST 'https://{your-domain}/api/campaigns/import' \ + curl -X POST 'https://example.com/api/campaigns/import' \ -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \ -H 'Content-Type: multipart/form-data' \ -F 'file=@/path/to/campaign_export.zip' @@ -128,7 +128,7 @@ Python example with JSON data import requests # API Endpoint - url = 'https://{your-domain}/api/campaigns/import' + url = 'https://example.com/api/campaigns/import' # Authentication headers = { @@ -167,7 +167,7 @@ Mautic supports two primary methods of API-based Campaign import: - Useful for creating new Campaigns or updating existing Campaigns .. important:: - - Replace ``{your-domain}`` with your actual Mautic instance domain + - Replace ``example.com`` with your actual Mautic instance domain - Ensure you have a valid access token by accessing the API Credentials section within Mautic's settings. - The imported Campaign must comply with Mautic's Campaign structure - Verify import permissions and data integrity From e19fd309eb7045f24025676f7826a54864da66f1 Mon Sep 17 00:00:00 2001 From: Ruth Cheesley <ruth@ruthcheesley.co.uk> Date: Tue, 4 Nov 2025 14:38:32 +0000 Subject: [PATCH 28/28] Update gitignore --- .gitignore | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 139 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 3a64ec25..828494f8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ -.DS_Store -**/.DS_Store + # Created by https://www.toptal.com/developers/gitignore/api/python,jupyternotebooks # Edit at https://www.toptal.com/developers/gitignore?templates=python,jupyternotebooks @@ -20,5 +19,143 @@ profile_default/ ipython_config.py +# Remove previous ipynb_checkpoints +# git rm -r .ipynb_checkpoints/ + ### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook + +# IPython + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ +# End of https://www.toptal.com/developers/gitignore/api/python,jupyternotebooks \ No newline at end of file