├── .all-contributorsrc ├── .devcontainer ├── devcontainer.json └── setup-project.sh ├── .github ├── dependabot.yml ├── holopin.yml ├── styles │ ├── Google │ │ ├── AMPM.yml │ │ ├── Acronyms.yml │ │ ├── Colons.yml │ │ ├── Contractions.yml │ │ ├── DateFormat.yml │ │ ├── Ellipses.yml │ │ ├── EmDash.yml │ │ ├── EnDash.yml │ │ ├── Exclamation.yml │ │ ├── FirstPerson.yml │ │ ├── Gender.yml │ │ ├── GenderBias.yml │ │ ├── HeadingPunctuation.yml │ │ ├── Headings.yml │ │ ├── Latin.yml │ │ ├── LyHyphens.yml │ │ ├── OptionalPlurals.yml │ │ ├── Ordinal.yml │ │ ├── OxfordComma.yml │ │ ├── Parens.yml │ │ ├── Passive.yml │ │ ├── Periods.yml │ │ ├── Quotes.yml │ │ ├── Ranges.yml │ │ ├── Semicolons.yml │ │ ├── Slang.yml │ │ ├── Spacing.yml │ │ ├── Spelling.yml │ │ ├── Units.yml │ │ ├── We.yml │ │ ├── Will.yml │ │ ├── WordList.yml │ │ ├── meta.json │ │ └── vocab.txt │ ├── Mautic │ │ └── FeatureList.yml │ └── config │ │ └── vocabularies │ │ └── Mautic │ │ └── accept.txt └── workflows │ └── linting.yml ├── .gitignore ├── .gitpod.Dockerfile ├── .gitpod.yml ├── .readthedocs.yaml ├── .vale.ini ├── .vscode ├── extensions.json ├── settings.json └── welcome.md ├── .well-known └── funding-manifest-urls ├── README.md └── docs ├── Makefile ├── _static ├── tablefix.css └── theme.css ├── _templates └── layout.html ├── code_samples ├── __init__.py ├── _main_code_sample.py └── helloworld_entity_world.py ├── code_samples_downloaded └── .gitkeep ├── components ├── api.rst ├── cache.rst ├── campaigns.rst ├── categories.rst ├── channels.rst ├── config.rst ├── contacts.rst ├── core.rst ├── emails.rst ├── forms.rst ├── forms_advanced.rst ├── images │ └── test-connection.png ├── integrations.rst ├── integrations_authentication.rst ├── integrations_builder.rst ├── integrations_configuration.rst ├── integrations_configuration_form_notes.rst ├── integrations_sync.rst ├── ip_lookups.rst ├── landing_pages.rst ├── maintenance.rst ├── points.rst ├── queue.rst ├── reports.rst ├── security.rst ├── tracking_script.rst ├── translator.rst └── ui.rst ├── conf.py ├── design ├── accordion.rst ├── availability.rst ├── displaying_elements_based_on_user_permissions.rst ├── feedback.rst ├── images │ ├── tile-clickable.png │ └── tile.png ├── labelling.rst ├── notifications.rst ├── protip.rst ├── quick_filters.rst ├── retrieving_system_information.rst ├── tiles.rst └── utilities.rst ├── development-environment ├── environments.rst ├── getting_started.rst ├── how_to_install_with_ddev.rst └── setup.rst ├── ext ├── code_samples_ext.py ├── phpdomain.py └── xref.py ├── form_hooks ├── general_hooks.rst ├── getting_started.rst ├── response_hooks.rst └── validation_hooks.rst ├── index.rst ├── links ├── __init__.py ├── ab_testing.py ├── action_based_di.py ├── builder_docs.py ├── codecov.py ├── codecov_gh_app.py ├── composer.py ├── composer_plugin.py ├── contributing_to_mautic.py ├── ddev_install.py ├── default_services_config.py ├── doctrine.py ├── doctrine_docs_migrations_bundle.py ├── doctrine_docs_orm.py ├── doctrine_docs_orm_annotations.py ├── doctrine_docs_orm_php_mapping.py ├── doctrine_docs_orm_query_builder.py ├── excluded_by_default_autowiring.py ├── github_actions.py ├── grapesjs_api.py ├── grapesjs_demo_plugin.py ├── grapesjs_plugins.py ├── hello_world_plugin.py ├── invokable_controllers.py ├── legacy_docs.py ├── link.py ├── manually_wiring_arguments.py ├── marketplace_allowlist_application_form.py ├── mautic_api_library.py ├── mautic_become_a_member.py ├── mautic_code_governance.py ├── mautic_contribution_docs.py ├── mautic_dev_contribution_guide.py ├── mautic_dev_forum.py ├── mautic_dev_portal.py ├── mautic_devdocs_issues.py ├── mautic_docs_mtc_js.py ├── mautic_end_user_docs.py ├── mautic_github.py ├── mautic_open_collective.py ├── mautic_php_to_twig.py ├── mautic_portal_product_team.py ├── mautic_portal_roadmap.py ├── mautic_project_governance.py ├── mautic_recommended_project.py ├── mautic_slack.py ├── mautic_supported_php_versions.py ├── mautic_tester_docs.py ├── mautic_upgrade_guide.py ├── mjml.py ├── packagist.py ├── php_cs_fixer.py ├── php_version_compare.py ├── phpstan.py ├── phpstan_baseline.py ├── phpunit.py ├── plugin_helloworld.py ├── plugin_helloworld_mapping_manunal.py ├── plugin_helloworld_order_executioner.py ├── plugin_helloworld_report_builder.py ├── plugin_helloworld_sync_data_exchange.py ├── plugin_integration_config_form_callback_interface.py ├── plugin_integration_guzzle_oauth2_subscriber.py ├── rector.py ├── request_bin.py ├── semantic_versioning_website.py ├── symfony_autoconfigure.py ├── symfony_autowiring.py ├── symfony_best_practices.py ├── symfony_cache_component.py ├── symfony_coding_standards.py ├── symfony_docs.py ├── symfony_docs_best_pratice_environments.py ├── symfony_docs_configuration_environments.py ├── symfony_docs_constraints.py ├── symfony_docs_custom_form_fields_type.py ├── symfony_docs_dynamic_form_modification.py ├── symfony_docs_event_subscribers.py ├── symfony_docs_form_classes.py ├── symfony_docs_form_validation.py ├── symfony_docs_form_validation_groups.py ├── symfony_docs_service_configurators.py ├── symfony_docs_service_decoration.py ├── symfony_docs_service_factories.py ├── symfony_docs_service_sythetic_services.py ├── symfony_docs_service_tags.py ├── symfony_docs_services_requiring_file_before_loading.py ├── symfony_framework.py ├── symfony_tag_console_command.py ├── symfony_tag_controller_service_arguments.py ├── symfony_tag_custom_form_field_type.py ├── symfony_tag_subscriber.py ├── translating_mautic.py ├── twig_docs.py ├── twig_lint.py └── webhook_cronjob.py ├── make.bat ├── marketplace ├── allowlist_what_and_why.rst ├── best_practices.rst ├── getting_started.rst └── listing.rst ├── plugins ├── autowiring.rst ├── config.rst ├── continuous-integration.rst ├── data.rst ├── dependencies.rst ├── event_listeners.rst ├── from-4-to-5.rst ├── getting_started.rst ├── installation.rst ├── mautic_vs_symfony.rst ├── structure.rst └── translations.rst ├── requirements.txt ├── rest_api ├── assets.rst ├── campaigns.rst ├── categories.rst ├── contacts.rst ├── fields.rst ├── notifications.rst ├── point_groups.rst ├── reports.rst └── text_messages.rst ├── testing ├── e2e_test_suite.rst └── images │ └── e2e_test_suite.png ├── themes ├── forms.rst ├── getting_started.rst ├── grapesjs.rst ├── legacy.rst └── system.rst └── webhooks ├── events ├── company_post_delete.rst ├── company_post_save.rst ├── email_on_open.rst ├── email_on_send.rst ├── form_on_submit.rst ├── index.rst ├── lead_channel_subscription_changed.rst ├── lead_company_change.rst ├── lead_points_change.rst ├── lead_post_delete.rst ├── lead_post_save_new.rst ├── lead_post_save_update.rst ├── page_on_hit.rst └── sms_on_send.rst ├── example_scripts.rst └── getting_started.rst /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "diaboloshogunate", 10 | "name": "Robert Parker", 11 | "avatar_url": "https://avatars.githubusercontent.com/u/25473863?v=4", 12 | "profile": "https://robert-parker.me", 13 | "contributions": [ 14 | "doc" 15 | ] 16 | }, 17 | { 18 | "login": "shinde-rahul", 19 | "name": "Rahul Shinde", 20 | "avatar_url": "https://avatars.githubusercontent.com/u/1046788?v=4", 21 | "profile": "https://github.com/shinde-rahul", 22 | "contributions": [ 23 | "doc" 24 | ] 25 | }, 26 | { 27 | "login": "dennisameling", 28 | "name": "Dennis Ameling", 29 | "avatar_url": "https://avatars.githubusercontent.com/u/17739158?v=4", 30 | "profile": "https://dennisameling.com", 31 | "contributions": [ 32 | "doc", 33 | "review" 34 | ] 35 | }, 36 | { 37 | "login": "ifeoluwafavour", 38 | "name": "Ife", 39 | "avatar_url": "https://avatars.githubusercontent.com/u/64481442?v=4", 40 | "profile": "http://ifeoluwafavour.hashnode.dev", 41 | "contributions": [ 42 | "doc" 43 | ] 44 | }, 45 | { 46 | "login": "sumbria", 47 | "name": "Balbinder Sumbria", 48 | "avatar_url": "https://avatars.githubusercontent.com/u/6416992?v=4", 49 | "profile": "https://incodit.com", 50 | "contributions": [ 51 | "doc" 52 | ] 53 | }, 54 | { 55 | "login": "Hugo-Prossaird", 56 | "name": "Hugo-Prossaird", 57 | "avatar_url": "https://avatars.githubusercontent.com/u/176997845?v=4", 58 | "profile": "https://github.com/Hugo-Prossaird", 59 | "contributions": [ 60 | "doc" 61 | ] 62 | } 63 | ], 64 | "contributorsPerLine": 7, 65 | "projectName": "developer-documentation-new", 66 | "projectOwner": "mautic", 67 | "repoType": "github", 68 | "repoHost": "https://github.com", 69 | "skipCi": true, 70 | "commitConvention": "angular", 71 | "commitType": "docs" 72 | } 73 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "features": { 3 | "ghcr.io/devcontainers/features/python:1": { 4 | "version": "3.11", 5 | "installTools": true 6 | } 7 | }, 8 | "customizations": { 9 | "vscode": { 10 | "extensions": [ 11 | "ms-python.python", 12 | "swyddfa.esbonio", 13 | "lextudio.restructuredtext", 14 | "eamodio.gitlens", 15 | "trond-snekvik.simple-rst" 16 | ] 17 | } 18 | }, 19 | "postCreateCommand": "chmod +x .devcontainer/setup-project.sh && .devcontainer/setup-project.sh" 20 | } 21 | -------------------------------------------------------------------------------- /.devcontainer/setup-project.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | VALE_VERSION=3.7.1 5 | 6 | # Install required packages 7 | pip install -r docs/requirements.txt 8 | 9 | # Required for Vale 10 | pip install rst2html rstcheck 11 | 12 | # Install Vale 13 | mkdir -p /tmp/vale 14 | curl -sSL https://github.com/errata-ai/vale/releases/download/v${VALE_VERSION}/vale_${VALE_VERSION}_Linux_64-bit.tar.gz | tar -xz -C /tmp/vale 15 | sudo install -o root -g root -m 0755 /tmp/vale/vale /usr/local/bin/vale 16 | rm -rf /tmp/vale 17 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "pip" 4 | directory: "/docs" # Location of package manifests 5 | schedule: 6 | interval: "daily" 7 | -------------------------------------------------------------------------------- /.github/holopin.yml: -------------------------------------------------------------------------------- 1 | organization: mautic 2 | holobytes: 3 | - evolvingStickerId: cm1ti4x4c57560cjq2styaitm 4 | icon: avocado 5 | alias: hacktoberfest-2024 6 | -------------------------------------------------------------------------------- /.github/styles/Google/AMPM.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Use 'AM' or 'PM' (preceded by a space)." 3 | link: 'https://developers.google.com/style/word-list' 4 | level: error 5 | nonword: true 6 | tokens: 7 | - '\d{1,2}[AP]M' 8 | - '\d{1,2} ?[ap]m' 9 | - '\d{1,2} ?[aApP]\.[mM]\.' 10 | -------------------------------------------------------------------------------- /.github/styles/Google/Acronyms.yml: -------------------------------------------------------------------------------- 1 | extends: conditional 2 | message: "Spell out '%s', if it's unfamiliar to the audience." 3 | link: 'https://developers.google.com/style/abbreviations' 4 | level: suggestion 5 | ignorecase: false 6 | # Ensures that the existence of 'first' implies the existence of 'second'. 7 | first: '\b([A-Z]{3,5})\b' 8 | second: '(?:\b[A-Z][a-z]+ )+\(([A-Z]{3,5})\)' 9 | # ... with the exception of these: 10 | exceptions: 11 | - API 12 | - ASP 13 | - CLI 14 | - CPU 15 | - CSS 16 | - CSV 17 | - DEBUG 18 | - DOM 19 | - DPI 20 | - FAQ 21 | - GCC 22 | - GDB 23 | - GET 24 | - GPU 25 | - GTK 26 | - GUI 27 | - HTML 28 | - HTTP 29 | - HTTPS 30 | - IDE 31 | - JAR 32 | - JSON 33 | - JSX 34 | - LESS 35 | - LLDB 36 | - NET 37 | - NOTE 38 | - NVDA 39 | - OSS 40 | - PATH 41 | - PDF 42 | - PHP 43 | - POST 44 | - RAM 45 | - REPL 46 | - RSA 47 | - SCM 48 | - SCSS 49 | - SDK 50 | - SQL 51 | - SSH 52 | - SSL 53 | - SVG 54 | - TBD 55 | - TCP 56 | - TODO 57 | - URI 58 | - URL 59 | - USB 60 | - UTF 61 | - XML 62 | - XSS 63 | - YAML 64 | - ZIP 65 | -------------------------------------------------------------------------------- /.github/styles/Google/Colons.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "'%s' should be in lowercase." 3 | link: 'https://developers.google.com/style/colons' 4 | nonword: true 5 | level: warning 6 | tokens: 7 | - ':\s[A-Z]' 8 | -------------------------------------------------------------------------------- /.github/styles/Google/Contractions.yml: -------------------------------------------------------------------------------- 1 | extends: substitution 2 | message: "Feel free to use '%s' instead of '%s'." 3 | link: 'https://developers.google.com/style/contractions' 4 | level: suggestion 5 | ignorecase: true 6 | action: 7 | name: replace 8 | swap: 9 | are not: aren't 10 | cannot: can't 11 | could not: couldn't 12 | did not: didn't 13 | do not: don't 14 | does not: doesn't 15 | has not: hasn't 16 | have not: haven't 17 | how is: how's 18 | is not: isn't 19 | it is: it's 20 | should not: shouldn't 21 | that is: that's 22 | they are: they're 23 | was not: wasn't 24 | we are: we're 25 | we have: we've 26 | were not: weren't 27 | what is: what's 28 | when is: when's 29 | where is: where's 30 | will not: won't 31 | -------------------------------------------------------------------------------- /.github/styles/Google/DateFormat.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Use 'July 31, 2016' format, not '%s'." 3 | link: 'https://developers.google.com/style/dates-times' 4 | ignorecase: true 5 | level: error 6 | nonword: true 7 | tokens: 8 | - '\d{1,2}(?:\.|/)\d{1,2}(?:\.|/)\d{4}' 9 | - '\d{1,2} (?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)|May|Jun(?:e)|Jul(?:y)|Aug(?:ust)|Sep(?:tember)?|Oct(?:ober)|Nov(?:ember)?|Dec(?:ember)?) \d{4}' 10 | -------------------------------------------------------------------------------- /.github/styles/Google/Ellipses.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "In general, don't use an ellipsis." 3 | link: 'https://developers.google.com/style/ellipses' 4 | nonword: true 5 | level: warning 6 | action: 7 | name: remove 8 | tokens: 9 | - '\.\.\.' 10 | -------------------------------------------------------------------------------- /.github/styles/Google/EmDash.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Don't put a space before or after a dash." 3 | link: 'https://developers.google.com/style/dashes' 4 | nonword: true 5 | level: error 6 | action: 7 | name: edit 8 | params: 9 | - remove 10 | - ' ' 11 | tokens: 12 | - '\s[—–]\s' 13 | -------------------------------------------------------------------------------- /.github/styles/Google/EnDash.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Use an em dash ('—') instead of '–'." 3 | link: 'https://developers.google.com/style/dashes' 4 | nonword: true 5 | level: error 6 | action: 7 | name: edit 8 | params: 9 | - replace 10 | - '-' 11 | - '—' 12 | tokens: 13 | - '–' 14 | -------------------------------------------------------------------------------- /.github/styles/Google/Exclamation.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Don't use exclamation points in text." 3 | link: 'https://developers.google.com/style/exclamation-points' 4 | nonword: true 5 | level: error 6 | tokens: 7 | - '\w!(?:\s|$)' 8 | -------------------------------------------------------------------------------- /.github/styles/Google/FirstPerson.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Avoid first-person pronouns such as '%s'." 3 | link: 'https://developers.google.com/style/pronouns#personal-pronouns' 4 | ignorecase: true 5 | level: warning 6 | nonword: true 7 | tokens: 8 | - (?:^|\s)I\s 9 | - (?:^|\s)I,\s 10 | - \bI'm\b 11 | - \bme\b 12 | - \bmy\b 13 | - \bmine\b 14 | -------------------------------------------------------------------------------- /.github/styles/Google/Gender.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Don't use '%s' as a gender-neutral pronoun." 3 | link: 'https://developers.google.com/style/pronouns#gender-neutral-pronouns' 4 | level: error 5 | ignorecase: true 6 | tokens: 7 | - he/she 8 | - s/he 9 | - \(s\)he 10 | -------------------------------------------------------------------------------- /.github/styles/Google/GenderBias.yml: -------------------------------------------------------------------------------- 1 | extends: substitution 2 | message: "Consider using '%s' instead of '%s'." 3 | link: 'https://developers.google.com/style/inclusive-documentation' 4 | ignorecase: true 5 | level: error 6 | swap: 7 | (?:alumna|alumnus): graduate 8 | (?:alumnae|alumni): graduates 9 | air(?:m[ae]n|wom[ae]n): pilot(s) 10 | anchor(?:m[ae]n|wom[ae]n): anchor(s) 11 | authoress: author 12 | camera(?:m[ae]n|wom[ae]n): camera operator(s) 13 | chair(?:m[ae]n|wom[ae]n): chair(s) 14 | congress(?:m[ae]n|wom[ae]n): member(s) of congress 15 | door(?:m[ae]|wom[ae]n): concierge(s) 16 | draft(?:m[ae]n|wom[ae]n): drafter(s) 17 | fire(?:m[ae]n|wom[ae]n): firefighter(s) 18 | fisher(?:m[ae]n|wom[ae]n): fisher(s) 19 | fresh(?:m[ae]n|wom[ae]n): first-year student(s) 20 | garbage(?:m[ae]n|wom[ae]n): waste collector(s) 21 | lady lawyer: lawyer 22 | ladylike: courteous 23 | landlord: building manager 24 | mail(?:m[ae]n|wom[ae]n): mail carriers 25 | man and wife: husband and wife 26 | man enough: strong enough 27 | mankind: human kind 28 | manmade: manufactured 29 | manpower: personnel 30 | men and girls: men and women 31 | middle(?:m[ae]n|wom[ae]n): intermediary 32 | news(?:m[ae]n|wom[ae]n): journalist(s) 33 | ombuds(?:man|woman): ombuds 34 | oneupmanship: upstaging 35 | poetess: poet 36 | police(?:m[ae]n|wom[ae]n): police officer(s) 37 | repair(?:m[ae]n|wom[ae]n): technician(s) 38 | sales(?:m[ae]n|wom[ae]n): salesperson or sales people 39 | service(?:m[ae]n|wom[ae]n): soldier(s) 40 | steward(?:ess)?: flight attendant 41 | tribes(?:m[ae]n|wom[ae]n): tribe member(s) 42 | waitress: waiter 43 | woman doctor: doctor 44 | woman scientist[s]?: scientist(s) 45 | work(?:m[ae]n|wom[ae]n): worker(s) 46 | -------------------------------------------------------------------------------- /.github/styles/Google/HeadingPunctuation.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Don't put a period at the end of a heading." 3 | link: 'https://developers.google.com/style/capitalization#capitalization-in-titles-and-headings' 4 | nonword: true 5 | level: warning 6 | scope: heading 7 | action: 8 | name: edit 9 | params: 10 | - remove 11 | - '.' 12 | tokens: 13 | - '[a-z0-9][.](?:\s|$)' 14 | -------------------------------------------------------------------------------- /.github/styles/Google/Headings.yml: -------------------------------------------------------------------------------- 1 | extends: capitalization 2 | message: "'%s' should use sentence-style capitalization." 3 | link: 'https://developers.google.com/style/capitalization#capitalization-in-titles-and-headings' 4 | level: warning 5 | scope: heading 6 | match: $sentence 7 | exceptions: 8 | - Azure 9 | - CLI 10 | - Code 11 | - Cosmos 12 | - Docker 13 | - Emmet 14 | - I 15 | - Kubernetes 16 | - Linux 17 | - macOS 18 | - Marketplace 19 | - MongoDB 20 | - REPL 21 | - Studio 22 | - TypeScript 23 | - URLs 24 | - Visual 25 | - VS 26 | - Windows 27 | -------------------------------------------------------------------------------- /.github/styles/Google/Latin.yml: -------------------------------------------------------------------------------- 1 | extends: substitution 2 | message: "Use '%s' instead of '%s'." 3 | link: 'https://developers.google.com/style/abbreviations' 4 | ignorecase: true 5 | level: error 6 | nonword: true 7 | action: 8 | name: replace 9 | swap: 10 | '\b(?:eg|e\.g\.)[\s,]': for example 11 | '\b(?:ie|i\.e\.)[\s,]': that is 12 | -------------------------------------------------------------------------------- /.github/styles/Google/LyHyphens.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "'%s' doesn't need a hyphen." 3 | link: 'https://developers.google.com/style/hyphens' 4 | level: error 5 | ignorecase: false 6 | nonword: true 7 | action: 8 | name: edit 9 | params: 10 | - replace 11 | - '-' 12 | - ' ' 13 | tokens: 14 | - '\s[^\s-]+ly-' 15 | -------------------------------------------------------------------------------- /.github/styles/Google/OptionalPlurals.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Don't use plurals in parentheses such as in '%s'." 3 | link: 'https://developers.google.com/style/plurals-parentheses' 4 | level: error 5 | nonword: true 6 | action: 7 | name: edit 8 | params: 9 | - remove 10 | - '(s)' 11 | tokens: 12 | - '\b\w+\(s\)' 13 | -------------------------------------------------------------------------------- /.github/styles/Google/Ordinal.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Spell out all ordinal numbers ('%s') in text." 3 | link: 'https://developers.google.com/style/numbers' 4 | level: error 5 | nonword: true 6 | tokens: 7 | - \d+(?:st|nd|rd|th) 8 | -------------------------------------------------------------------------------- /.github/styles/Google/OxfordComma.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Use the Oxford comma in '%s'." 3 | link: 'https://developers.google.com/style/commas' 4 | scope: sentence 5 | level: warning 6 | tokens: 7 | - '(?:[^,]+,){1,}\s\w+\s(?:and|or)' 8 | -------------------------------------------------------------------------------- /.github/styles/Google/Parens.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Use parentheses judiciously." 3 | link: 'https://developers.google.com/style/parentheses' 4 | nonword: true 5 | level: suggestion 6 | tokens: 7 | - '\(.+\)' 8 | -------------------------------------------------------------------------------- /.github/styles/Google/Passive.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | link: 'https://developers.google.com/style/voice' 3 | message: "In general, use active voice instead of passive voice ('%s')." 4 | ignorecase: true 5 | level: suggestion 6 | raw: 7 | - \b(am|are|were|being|is|been|was|be)\b\s* 8 | tokens: 9 | - '[\w]+ed' 10 | - awoken 11 | - beat 12 | - become 13 | - been 14 | - begun 15 | - bent 16 | - beset 17 | - bet 18 | - bid 19 | - bidden 20 | - bitten 21 | - bled 22 | - blown 23 | - born 24 | - bought 25 | - bound 26 | - bred 27 | - broadcast 28 | - broken 29 | - brought 30 | - built 31 | - burnt 32 | - burst 33 | - cast 34 | - caught 35 | - chosen 36 | - clung 37 | - come 38 | - cost 39 | - crept 40 | - cut 41 | - dealt 42 | - dived 43 | - done 44 | - drawn 45 | - dreamt 46 | - driven 47 | - drunk 48 | - dug 49 | - eaten 50 | - fallen 51 | - fed 52 | - felt 53 | - fit 54 | - fled 55 | - flown 56 | - flung 57 | - forbidden 58 | - foregone 59 | - forgiven 60 | - forgotten 61 | - forsaken 62 | - fought 63 | - found 64 | - frozen 65 | - given 66 | - gone 67 | - gotten 68 | - ground 69 | - grown 70 | - heard 71 | - held 72 | - hidden 73 | - hit 74 | - hung 75 | - hurt 76 | - kept 77 | - knelt 78 | - knit 79 | - known 80 | - laid 81 | - lain 82 | - leapt 83 | - learnt 84 | - led 85 | - left 86 | - lent 87 | - let 88 | - lighted 89 | - lost 90 | - made 91 | - meant 92 | - met 93 | - misspelt 94 | - mistaken 95 | - mown 96 | - overcome 97 | - overdone 98 | - overtaken 99 | - overthrown 100 | - paid 101 | - pled 102 | - proven 103 | - put 104 | - quit 105 | - read 106 | - rid 107 | - ridden 108 | - risen 109 | - run 110 | - rung 111 | - said 112 | - sat 113 | - sawn 114 | - seen 115 | - sent 116 | - set 117 | - sewn 118 | - shaken 119 | - shaven 120 | - shed 121 | - shod 122 | - shone 123 | - shorn 124 | - shot 125 | - shown 126 | - shrunk 127 | - shut 128 | - slain 129 | - slept 130 | - slid 131 | - slit 132 | - slung 133 | - smitten 134 | - sold 135 | - sought 136 | - sown 137 | - sped 138 | - spent 139 | - spilt 140 | - spit 141 | - split 142 | - spoken 143 | - spread 144 | - sprung 145 | - spun 146 | - stolen 147 | - stood 148 | - stridden 149 | - striven 150 | - struck 151 | - strung 152 | - stuck 153 | - stung 154 | - stunk 155 | - sung 156 | - sunk 157 | - swept 158 | - swollen 159 | - sworn 160 | - swum 161 | - swung 162 | - taken 163 | - taught 164 | - thought 165 | - thrived 166 | - thrown 167 | - thrust 168 | - told 169 | - torn 170 | - trodden 171 | - understood 172 | - upheld 173 | - upset 174 | - wed 175 | - wept 176 | - withheld 177 | - withstood 178 | - woken 179 | - won 180 | - worn 181 | - wound 182 | - woven 183 | - written 184 | - wrung 185 | -------------------------------------------------------------------------------- /.github/styles/Google/Periods.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Don't use periods with acronyms or initialisms such as '%s'." 3 | link: 'https://developers.google.com/style/abbreviations' 4 | level: error 5 | nonword: true 6 | tokens: 7 | - '\b(?:[A-Z]\.){3,}' 8 | -------------------------------------------------------------------------------- /.github/styles/Google/Quotes.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Commas and periods go inside quotation marks." 3 | link: 'https://developers.google.com/style/quotation-marks' 4 | level: error 5 | nonword: true 6 | tokens: 7 | - '"[^"]+"[.,?]' 8 | -------------------------------------------------------------------------------- /.github/styles/Google/Ranges.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Don't add words such as 'from' or 'between' to describe a range of numbers." 3 | link: 'https://developers.google.com/style/hyphens' 4 | nonword: true 5 | level: warning 6 | tokens: 7 | - '(?:from|between)\s\d+\s?-\s?\d+' 8 | -------------------------------------------------------------------------------- /.github/styles/Google/Semicolons.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Use semicolons judiciously." 3 | link: 'https://developers.google.com/style/semicolons' 4 | nonword: true 5 | scope: sentence 6 | level: suggestion 7 | tokens: 8 | - ';' 9 | -------------------------------------------------------------------------------- /.github/styles/Google/Slang.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Don't use internet slang abbreviations such as '%s'." 3 | link: 'https://developers.google.com/style/abbreviations' 4 | ignorecase: true 5 | level: error 6 | tokens: 7 | - 'tl;dr' 8 | - ymmv 9 | - rtfm 10 | - imo 11 | - fwiw 12 | -------------------------------------------------------------------------------- /.github/styles/Google/Spacing.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "'%s' should have one space." 3 | link: 'https://developers.google.com/style/sentence-spacing' 4 | level: error 5 | nonword: true 6 | tokens: 7 | - '[a-z][.?!] {2,}[A-Z]' 8 | - '[a-z][.?!][A-Z]' 9 | -------------------------------------------------------------------------------- /.github/styles/Google/Spelling.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "In general, use American spelling instead of '%s'." 3 | link: 'https://developers.google.com/style/spelling' 4 | ignorecase: true 5 | level: warning 6 | tokens: 7 | - '(?:\w+)nised?' 8 | - '(?:\w+)logue' 9 | -------------------------------------------------------------------------------- /.github/styles/Google/Units.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Put a nonbreaking space between the number and the unit in '%s'." 3 | link: 'https://developers.google.com/style/units-of-measure' 4 | nonword: true 5 | level: error 6 | tokens: 7 | - \d+(?:B|kB|MB|GB|TB) 8 | - \d+(?:ns|ms|s|min|h|d) 9 | -------------------------------------------------------------------------------- /.github/styles/Google/We.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Try to avoid using first-person plural like '%s'." 3 | link: 'https://developers.google.com/style/pronouns#personal-pronouns' 4 | level: warning 5 | ignorecase: true 6 | tokens: 7 | - we 8 | - we'(?:ve|re) 9 | - ours? 10 | - us 11 | - let's 12 | -------------------------------------------------------------------------------- /.github/styles/Google/Will.yml: -------------------------------------------------------------------------------- 1 | extends: existence 2 | message: "Avoid using '%s'." 3 | link: 'https://developers.google.com/style/tense' 4 | ignorecase: true 5 | level: warning 6 | tokens: 7 | - will 8 | -------------------------------------------------------------------------------- /.github/styles/Google/WordList.yml: -------------------------------------------------------------------------------- 1 | extends: substitution 2 | message: "Use '%s' instead of '%s'." 3 | link: 'https://developers.google.com/style/word-list' 4 | level: warning 5 | ignorecase: false 6 | action: 7 | name: replace 8 | swap: 9 | '(?:API Console|dev|developer) key': API key 10 | '(?:cell ?phone|smart ?phone)': phone|mobile phone 11 | '(?:dev|developer|APIs) console': API console 12 | '(?:e-mail|E-mail)': email 13 | '(?:file ?path|path ?name)': path 14 | '(?:kill|terminate|abort)': stop|exit|cancel|end 15 | '(?:OAuth ?2|Oauth)': OAuth 2.0 16 | '(?:ok|Okay)': OK|okay 17 | '(?:WiFi|wifi)': Wi-Fi 18 | '[\.]+apk': APK 19 | '3\-D': 3D 20 | 'Google (?:I\-O|IO)': Google I/O 21 | 'tap (?:&|and) hold': touch & hold 22 | 'un(?:check|select)': clear 23 | above: preceding 24 | account name: username 25 | action bar: app bar 26 | admin: administrator 27 | Ajax: AJAX 28 | Android device: Android-powered device 29 | android: Android 30 | API explorer: APIs Explorer 31 | application: app 32 | approx\.: approximately 33 | authN: authentication 34 | authZ: authorization 35 | autoupdate: automatically update 36 | cellular data: mobile data 37 | cellular network: mobile network 38 | chapter: documents|pages|sections 39 | check box: checkbox 40 | check: select 41 | CLI: command-line tool 42 | click on: click|click in 43 | Cloud: Google Cloud Platform|GCP 44 | Container Engine: Kubernetes Engine 45 | content type: media type 46 | curated roles: predefined roles 47 | data are: data is 48 | Developers Console: Google API Console|API Console 49 | disabled?: turn off|off 50 | ephemeral IP address: ephemeral external IP address 51 | fewer data: less data 52 | file name: filename 53 | firewalls: firewall rules 54 | functionality: capability|feature 55 | Google account: Google Account 56 | Google accounts: Google Accounts 57 | Googling: search with Google 58 | grayed-out: unavailable 59 | HTTPs: HTTPS 60 | in order to: to 61 | ingest: import|load 62 | k8s: Kubernetes 63 | long press: touch & hold 64 | network IP address: internal IP address 65 | omnibox: address bar 66 | open-source: open source 67 | overview screen: recents screen 68 | regex: regular expression 69 | SHA1: SHA-1|HAS-SHA1 70 | sign into: sign in to 71 | sign-?on: single sign-on 72 | static IP address: static external IP address 73 | stylesheet: style sheet 74 | synch: sync 75 | tablename: table name 76 | tablet: device 77 | touch: tap 78 | url: URL 79 | vs\.: versus 80 | World Wide Web: web 81 | -------------------------------------------------------------------------------- /.github/styles/Google/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "feed": "https://github.com/errata-ai/Google/releases.atom", 3 | "vale_version": ">=1.0.0" 4 | } 5 | -------------------------------------------------------------------------------- /.github/styles/Google/vocab.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mautic/developer-documentation-new/bbfa70eea02830aadb2ec5a4dd8c28145291a1a8/.github/styles/Google/vocab.txt -------------------------------------------------------------------------------- /.github/styles/Mautic/FeatureList.yml: -------------------------------------------------------------------------------- 1 | extends: substitution 2 | message: "Is this referring to a Mautic feature? If so, use '%s' instead of '%s'." 3 | level: suggestion 4 | ignorecase: true 5 | action: 6 | name: replace 7 | swap: 8 | asset: Asset 9 | assets: Assets 10 | campaign: Campaign 11 | campaigns: Campaigns 12 | category: Category 13 | categories: Categories 14 | channel: Channel 15 | channels: Channels 16 | companies: Companies 17 | company: Company 18 | component: Component 19 | components: Components 20 | contact: Contact 21 | contacts: Contacts 22 | custom field: Custom Field 23 | custom fields: Custom Fields 24 | dwc: Dynamic Content 25 | dynamic content: Dynamic Content 26 | email: Email 27 | emails: Emails 28 | focus item: Focus Item 29 | focus items: Focus Items 30 | form: Form 31 | form field: Form Field 32 | form fields: Form Fields 33 | forms: Forms 34 | integration: Integration 35 | integrations: Integrations 36 | landing page: Landing Page 37 | landing pages: Landing Pages 38 | lead: Contact 39 | leads: Contacts 40 | marketing message: Marketing Message 41 | marketing messages: Marketing Messages 42 | page: Landing Page 43 | pages: Landing Pages 44 | point actions: Point Actions 45 | point triggers: Point Triggers 46 | points: Points 47 | plugin: Plugin 48 | plugins: Plugins 49 | report: Report 50 | reports: Reports 51 | role: Role 52 | roles: Roles 53 | segment: Segment 54 | segments: Segments 55 | stage: Stage 56 | stages: Stages 57 | sync engine: Sync Engine 58 | sync report: Sync Report 59 | theme: Theme 60 | themes: Themes 61 | user: User 62 | users: Users 63 | webhook: Webhook 64 | webhooks: Webhooks -------------------------------------------------------------------------------- /.github/styles/config/vocabularies/Mautic/accept.txt: -------------------------------------------------------------------------------- 1 | AJAX 2 | allowlist 3 | Amazon SES 4 | Ameling 5 | ascenders 6 | autoloader 7 | Autoloader 8 | autowire 9 | autowired 10 | Autowired 11 | autowires 12 | autowiring 13 | Autowiring 14 | bcc 15 | Beanstalkd 16 | Bing 17 | bitwise 18 | blocklist 19 | boolean 20 | booleans 21 | cc 22 | CCPA 23 | Citrix 24 | Clearbit 25 | CloudAMQP 26 | Codeception 27 | Codecov 28 | Company(ies) 29 | Composability 30 | Composer 31 | Config 32 | config 33 | configurator 34 | ConnectWise 35 | Contact Points 36 | Continuous Integration 37 | Contribution(s) 38 | CORS 39 | cron 40 | Cron 41 | CTRL 42 | DDEV 43 | DNC 44 | Do Not Contact 45 | Dripflow 46 | Dynamic Web Content 47 | FALSE 48 | false 49 | Firewalls 50 | firewalls 51 | Focus Item 52 | Focus Items 53 | Froala 54 | Froogaloop 55 | gcm 56 | GIF 57 | GitBook 58 | GitHub 59 | Gitpod 60 | GMail 61 | GrapesJS Builder 62 | Grav 63 | Gravatar 64 | Hammerschmied 65 | href 66 | html 67 | HTML 68 | http 69 | https 70 | HubSpot 71 | IDP 72 | IMAP 73 | infographics 74 | Initialisms 75 | initialisms 76 | ISO 77 | JavaScript 78 | Joomla 79 | Legacy Builder 80 | Libre 81 | Licensor 82 | LinkedIn 83 | Mailjet 84 | Marketing Messages 85 | Mautic 86 | MaxMind 87 | middleware 88 | Middlewares 89 | middlewares 90 | MJML 91 | Multiselect 92 | multiselect 93 | MVP 94 | mysqldump 95 | Namespace 96 | namespace 97 | Node.js 98 | noindex 99 | OAuth 100 | OAuth1a 101 | OAuth2 102 | Packagist 103 | patch 104 | PATCH 105 | PHP 106 | PhpStorm 107 | PHPUnit.xml 108 | Pipedrive 109 | Piwik 110 | Plugin 111 | Plugins 112 | post 113 | POST 114 | ProTip 115 | ProTips 116 | PUT 117 | PyCharm 118 | Rackspace 119 | Rahul 120 | Read the Docs 121 | Remarketing 122 | Report Email 123 | Salesforce 124 | SAML 125 | Schimpf 126 | SCP 127 | Segment Email 128 | SendGrid 129 | SES 130 | Shinde 131 | SMS 132 | SMTP 133 | SNS 134 | SparkPost 135 | SSO 136 | sublicense 137 | SugarCRM 138 | Suhosin 139 | Symfony 140 | themes 141 | Themes 142 | timeframe 143 | Todo 144 | tooltip 145 | Transifex 146 | Translator 147 | TRUE 148 | true 149 | Twilio 150 | unassign 151 | unpublish 152 | Unpublish 153 | unsubscription 154 | URIs 155 | URL 156 | URLs 157 | US 158 | UTM 159 | Vtiger 160 | Webhooks 161 | Webmecanik 162 | www 163 | XPath 164 | YAML 165 | Zapier 166 | Zoho 167 | -------------------------------------------------------------------------------- /.github/workflows/linting.yml: -------------------------------------------------------------------------------- 1 | name: Linting 2 | on: 3 | pull_request: 4 | 5 | jobs: 6 | prose: 7 | runs-on: ubuntu-22.04 # See https://github.com/errata-ai/vale-action/issues/128 before upgrading 8 | permissions: 9 | contents: read 10 | pull-requests: write 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v4 14 | with: 15 | fetch-depth: 0 16 | 17 | - uses: actions/setup-python@v4 18 | with: 19 | python-version: '3.x' 20 | cache: 'pip' 21 | 22 | - name: Install Python dependencies 23 | run: pip3 install -r docs/requirements.txt 24 | 25 | - name: Vale 26 | uses: errata-ai/vale-action@reviewdog 27 | with: 28 | version: 3.7.1 # Please keep version in sync with the version in .gitpod.Dockerfile for a consistent experience 29 | env: 30 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 31 | 32 | build: 33 | runs-on: ubuntu-latest 34 | steps: 35 | - name: Checkout 36 | uses: actions/checkout@v4 37 | 38 | - uses: actions/setup-python@v4 39 | with: 40 | python-version: '3.x' 41 | cache: 'pip' 42 | 43 | - name: Install Python dependencies 44 | run: pip3 install -r docs/requirements.txt 45 | 46 | - name: Build docs 47 | working-directory: docs 48 | run: make html 49 | 50 | - name: Check links 51 | working-directory: docs 52 | run: make checklinks 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .idea 3 | __pycache__ 4 | .DS_Store 5 | docs/code_samples_downloaded/* 6 | !docs/code_samples_downloaded/.gitkeep 7 | -------------------------------------------------------------------------------- /.gitpod.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.11 2 | 3 | # Don't update to a higher version until this issue has been fixed: https://github.com/errata-ai/vale/issues/528 4 | # Please keep version in sync with the version in .github/workflows/linting.yml for a consistent experience 5 | ENV VALE_VERSION=3.7.1 6 | 7 | WORKDIR /workspace 8 | 9 | # Needed for Vale (rst2html) and reStructuredText (rstcheck) 10 | RUN pip install rst2html rstcheck 11 | 12 | RUN mkdir -p vale && cd vale && wget https://github.com/errata-ai/vale/releases/download/v${VALE_VERSION}/vale_${VALE_VERSION}_Linux_64-bit.tar.gz && \ 13 | tar -xf vale_${VALE_VERSION}_Linux_64-bit.tar.gz && cp /workspace/vale/vale /usr/local/bin/vale && cd ../ 14 | 15 | # /home/gitpod/.local/bin ensures that Python packages like rstcheck can be found 16 | ENV PATH=/home/gitpod/.local/bin:$PATH 17 | 18 | # Create the gitpod user. UID must be 33333. https://www.gitpod.io/docs/configure/workspaces/workspace-image#use-a-custom-dockerfile 19 | RUN useradd -l -u 33333 -G sudo -md /home/gitpod -s /bin/bash -p gitpod gitpod 20 | 21 | USER gitpod 22 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | image: 2 | file: .gitpod.Dockerfile 3 | 4 | tasks: 5 | - before: pip install -r docs/requirements.txt 6 | command: gp open .vscode/welcome.md 7 | 8 | vscode: 9 | extensions: 10 | - ms-python.python 11 | - lextudio.restructuredtext@190.1.4 # See https://github.com/mautic/user-documentation/pull/334#issuecomment-2405922370 before upgrading. - errata-ai.vale-server 12 | - eamodio.gitlens 13 | - trond-snekvik.simple-rst 14 | 15 | ports: 16 | - port: 3000 17 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Set the version of Python and other tools you might need 9 | build: 10 | os: ubuntu-20.04 11 | tools: 12 | python: "3.11" 13 | # You can also specify other tool versions: 14 | # nodejs: "16" 15 | # rust: "1.55" 16 | # golang: "1.17" 17 | 18 | # Build documentation in the docs/ directory with Sphinx 19 | sphinx: 20 | configuration: docs/conf.py 21 | 22 | # If using Sphinx, optionally build your docs in additional formats such as PDF 23 | # formats: 24 | # - pdf 25 | 26 | # Optionally declare the Python requirements required to build your docs 27 | python: 28 | install: 29 | - requirements: docs/requirements.txt 30 | -------------------------------------------------------------------------------- /.vale.ini: -------------------------------------------------------------------------------- 1 | StylesPath = .github/styles 2 | Vocab = Mautic 3 | MinAlertLevel = suggestion 4 | [*.{md,rst}] 5 | BasedOnStyles = Vale, Google, Mautic 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "trond-snekvik.simple-rst", 4 | "errata-ai.vale-server", 5 | "ms-python.python", 6 | "eamodio.gitlens", 7 | "lextudio.restructuredtext" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "esbonio.server.enabled": true, 3 | "gitlens.showWelcomeOnInstall": false, 4 | "esbonio.sphinx.buildDir": "${workspaceFolder}/build", 5 | "esbonio.sphinx.confDir": "${workspaceFolder}/docs", 6 | "restructuredtext.pythonRecommendation.disabled": true, 7 | "restructuredtext.preview.name": "sphinx", 8 | "esbonio.sphinx.pythonCommand": "python3", 9 | "esbonio.sphinx.buildCommand": "sphinx-build", 10 | "esbonio.logging.level": "debug", 11 | "gitlens.showWhatsNewAfterUpgrades": false 12 | } 13 | -------------------------------------------------------------------------------- /.vscode/welcome.md: -------------------------------------------------------------------------------- 1 | Welcome to the Mautic developer documentation editor. 2 | 3 | In this repository, you should already have set up everything you need. To get started, follow these steps: 4 | 1. Open a file in the `docs/` folder - files that end with `.rst` are docs files 5 | 2. Click the magnifier icon in the top right corner to see a live preview of the file you're editing 6 | 7 | Pretty cool, right? 8 | 9 | Happy writing :) 10 | -------------------------------------------------------------------------------- /.well-known/funding-manifest-urls: -------------------------------------------------------------------------------- 1 | https://github.com/mautic/mautic/blob/5.x/funding.json 2 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = 9 | BUILDDIR = build 10 | LINKSDIR = links 11 | LINKCHECKDIR = build/linkcheck 12 | CODESAMPLESDIR = code_samples 13 | 14 | # Put it first so that "make" without argument is like "make help". 15 | help: 16 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 17 | 18 | .PHONY: help Makefile 19 | 20 | .PHONY: link 21 | link: 22 | @read -p "Enter a Unique Link Name: " link_name; \ 23 | read -p "Enter the link text the user sees: " link_text; \ 24 | read -p "Enter the URL: " link_url; \ 25 | read -p "Enter the .py file name (use_lower_case_and_underscore of link name): " file_name; \ 26 | echo "The link name is: " $$link_name; \ 27 | echo "The link text is: " $$link_text; \ 28 | echo "The URL is: " $$link_url; \ 29 | echo "Creating the file: " $(LINKSDIR)/$$file_name".py"; \ 30 | echo "Enter the link in content as :xref:\`"$$link_name"\`"; \ 31 | echo "The user will see:" $$link_text; \ 32 | echo "Make sure you build and test the link."; \ 33 | echo "from . import link\n\nlink_name = \"$$link_name\" \n\ 34 | link_text = \"$$link_text\" \n\ 35 | link_url = \"$$link_url\" \n\n\ 36 | link.xref_links.update({link_name: (link_text, link_url)})" \ 37 | > $(LINKSDIR)/$$file_name".py" 38 | 39 | .PHONY: code-sample 40 | code-sample: 41 | @read -p "Enter a Unique File Name (with .php suffix): " file_name; \ 42 | read -p "Enter the URL to the file (should start with https://raw.githubusercontent.com/...): " file_url; \ 43 | read -p "Enter the .py file name (use_lower_case_and_underscore of file name): " py_file_name; \ 44 | echo "The file name is: " $$file_name; \ 45 | echo "The URL is: " $$file_url; \ 46 | echo "The .py file name is: " $$py_file_name; \ 47 | echo "Creating the file: " $(CODESAMPLESDIR)/$$py_file_name".py"; \ 48 | echo "Enter the link in content as .. literalinclude:: ../code_samples_downloaded/"$$file_name; \ 49 | echo "Make sure you build and test the code sample."; \ 50 | echo "from . import _main_code_sample\n\noutput_file_name = \"$$file_name\" \n\ 51 | url = \"$$file_url\" \n\n\ 52 | _main_code_sample.code_samples.update({output_file_name: (url)})" \ 53 | > $(CODESAMPLESDIR)/$$py_file_name".py" 54 | 55 | .PHONY: checklinks 56 | checklinks: 57 | $(SPHINXBUILD) -b linkcheck . $(LINKCHECKDIR) 58 | @echo 59 | @echo "Check finished. Report is in $(LINKCHECKDIR)." 60 | 61 | # Catch-all target: route all unknown targets to Sphinx using the new 62 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 63 | %: Makefile 64 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 65 | -------------------------------------------------------------------------------- /docs/_static/tablefix.css: -------------------------------------------------------------------------------- 1 | /* override table width restrictions */ 2 | .wy-table-responsive table td, .wy-table-responsive table th { 3 | white-space: normal; 4 | } 5 | 6 | .wy-table-responsive { 7 | margin-bottom: 24px; 8 | max-width: 100%; 9 | overflow: visible; 10 | } 11 | 12 | .wy-table-responsive th p { 13 | margin-bottom: unset; 14 | } -------------------------------------------------------------------------------- /docs/_static/theme.css: -------------------------------------------------------------------------------- 1 | .wy-side-nav-search, .wy-nav-side, .wy-nav-top { 2 | background-color: #4e5e9e; 3 | } 4 | 5 | .wy-menu-vertical p.caption { 6 | color: #fdb933; 7 | } 8 | 9 | a { 10 | color: #4e5e9e; 11 | } 12 | 13 | .wy-menu-vertical a, .wy-side-nav-search>a { 14 | color: #ffffff; 15 | } -------------------------------------------------------------------------------- /docs/_templates/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "!layout.html" %} 2 | {% set css_files = css_files + ["_static/tablefix.css"] + ["_static/theme.css"] %} -------------------------------------------------------------------------------- /docs/code_samples/__init__.py: -------------------------------------------------------------------------------- 1 | from os.path import dirname, basename, isfile 2 | 3 | import glob 4 | # Walk through all files in this folder 5 | modules = glob.glob(dirname(__file__)+"/*.py") 6 | 7 | __all__ = [ basename(f)[:-3] for f in modules if isfile(f)] -------------------------------------------------------------------------------- /docs/code_samples/_main_code_sample.py: -------------------------------------------------------------------------------- 1 | # We need this Python dictionary so that other files in this folder can extend it. 2 | code_samples = {} -------------------------------------------------------------------------------- /docs/code_samples/helloworld_entity_world.py: -------------------------------------------------------------------------------- 1 | from . import _main_code_sample 2 | 3 | # Output file name must be unique! 4 | output_file_name = "Entity_World.php" 5 | # Ensure that the URL always starts with https://raw.githubusercontent.com/... 6 | url = "https://raw.githubusercontent.com/mautic/plugin-helloworld/mautic-4/Entity/World.php" 7 | 8 | _main_code_sample.code_samples.update({output_file_name: (url)}) 9 | -------------------------------------------------------------------------------- /docs/code_samples_downloaded/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mautic/developer-documentation-new/bbfa70eea02830aadb2ec5a4dd8c28145291a1a8/docs/code_samples_downloaded/.gitkeep -------------------------------------------------------------------------------- /docs/components/api.rst: -------------------------------------------------------------------------------- 1 | API 2 | ### 3 | 4 | To add custom API endpoints, define the routes under the API firewall in the :doc:`Plugin's config file`. 5 | This places the route behind ``/api`` which is only accessible to authorized Users. 6 | 7 | .. code-block:: php 8 | 9 | [ 18 | 19 | // ... 20 | 21 | 'controllers' => [ 22 | 'plugin.hello_world.controller.api' => [ 23 | 'class' => \MauticPlugin\HelloWorldBundle\Controller\ApiController::class, 24 | 'arguments' => [ 25 | 'mautic.security', 26 | 'plugin.hello_world.model.worlds' 27 | ], 28 | 'methodCalls' => [ 29 | 'setContainer' => [ 30 | '@service_container', 31 | ], 32 | ], 33 | ], 34 | ], 35 | ], 36 | 37 | 'routes' => [ 38 | 39 | // ... 40 | 41 | 'api' => [ 42 | 'plugin_helloworld_api' => [ 43 | 'path' => '/hello/worlds', 44 | 'controller' => 'HelloWorldBundle:Api:worlds', 45 | 'method' => 'GET' 46 | ] 47 | ] 48 | ], 49 | 50 | // ... 51 | ]; 52 | 53 | The API controller should extend ``Mautic\ApiBundle\Controller\CommonApiController`` to leverage the helper methods provided. 54 | 55 | .. code-block:: php 56 | 57 | corePermissions = $corePermissions; 78 | $this->worldsModel = $worldsModel; 79 | } 80 | 81 | /** 82 | * Get a list of worlds 83 | */ 84 | public function getWorldsAction(Request $request): Response 85 | { 86 | if (!$this->corePermissions->isGranted('plugin:helloWorld:worlds:view')) { 87 | return $this->accessDenied(); 88 | } 89 | 90 | $filter = $request->query->get('filter', null); 91 | $limit = $request->query->get('limit', null); 92 | $start = $request->query->get('start', null); 93 | 94 | $worlds = $this->model->getWorlds($filter, $limit, $start); 95 | $worlds = $this->view($worlds, 200); 96 | 97 | return $this->handleView($worlds); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /docs/components/cache.rst: -------------------------------------------------------------------------------- 1 | Cache 2 | ##### 3 | 4 | Symfony makes heavy use of a filesystem cache. When developing for Mautic, clearing the cache is a regular occurrence. By default, Mautic instances have the cache located in ``var/cache/ENV`` where ``ENV`` is the environment currently accessed - ``dev`` or ``prod``. To rebuild the cache, delete the relevant ``ENV`` folder within the cache directory, or run the Symfony command ``php bin/console cache:clear --env=ENV``. If a specific environment isn't passed to the command via ``--env=ENV``, Mautic uses the ``dev`` environment by default. 5 | 6 | 7 | .. vale off 8 | 9 | In the ``dev`` environment, Mautic doesn't cache translations, views, and assets. However, changes to these files require clearing the cache for them to take effect in the ``prod`` environment. Changes to Mautic config files, Symfony config files, etc., require clearing of the cache regardless of the environment. 10 | 11 | .. vale on 12 | 13 | The typical rule of thumb is, if Mautic isn't acting as you expect after making changes, try clearing your cache. If you get ``class could not be found`` or ``cannot redeclare class`` errors when using the ``cache:clear`` command, manually delete the ``var/cache/ENV`` folder - replacing ENV with the environment e.g ``dev`` or ``prod`` - then run the command and/or browse to the site to rebuild. 14 | 15 | Cache bundle 16 | ************ 17 | 18 | Enables PSR-6 and PSR-16 caching. Check :xref:`Symfony Cache Component` 19 | 20 | Namespace versus tag 21 | ==================== 22 | 23 | This bundle introduces tags to the cache. All its adapters are fully tag aware which makes the use of namespace obsolete for daily use. 24 | 25 | Previously, if you wanted to keep control on cache section and didn't want to hold the index of all keys to clear, you would have to use namespace. 26 | 27 | The main disadvantage of this approach is that Mautic creates a new adapter for each namespace. 28 | 29 | From Symfony 3.4, the cache uses tag-aware adapters. If you want to clear all records related to your Bundle or Component, you just need to tag them. 30 | 31 | .. code-block:: php 32 | 33 | /** @var CacheProvider $cache */ 34 | $cache = $this->get('mautic.cache.provider'); 35 | /** @var CacheItemInterface $item */ 36 | $item = $cache->getItem('test_tagged_Item'); 37 | $item->set('yesa!!!'); 38 | $item->tag(['firstTag', 'secondTag']); 39 | $item->expiresAfter(20000); 40 | 41 | All you need to do now is to clear all tagged items: 42 | 43 | .. code-block:: php 44 | 45 | $cache->invalidateTags(['firstTag']); 46 | 47 | Pools clearing 48 | ============== 49 | 50 | Removing cache items 51 | -------------------- 52 | 53 | Cache Pools include methods to delete a cache item, some of them, or all of them. The most common is ``Psr\\Cache\\CacheItemPoolInterface::deleteItem``, which deletes the cache item identified by the given key. 54 | 55 | .. code-block:: php 56 | 57 | $isDeleted = $cache->deleteItem('user_'.$userId); 58 | 59 | Use the ``Psr\\Cache\\CacheItemPoolInterface::deleteItems`` method to delete several cache items simultaneously - it returns true only if all the items have been deleted, even when any or some of them don't exist. 60 | 61 | Configuration 62 | ------------- 63 | 64 | Plugins come preconfigured to utilize filesystem caching. 65 | 66 | These are the default settings: 67 | 68 | .. code-block:: php 69 | 70 | 'cache_adapter' => 'mautic.cache.adapter.filesystem', 71 | 'cache_prefix' => 'app', 72 | 'cache_lifetime' => 86400 73 | 74 | They can be overridden in ``local.php`` like this: 75 | 76 | .. code-block:: php 77 | 78 | 'cache_adapter' => 'mautic.cache.adapter.redis', 79 | 'cache_prefix' => 'app_cache', 80 | 'cache_lifetime' => 86400, 81 | 82 | Delivered adapters 83 | ------------------ 84 | .. vale off 85 | 86 | - ``mautic.cache.adapter.filesystem`` 87 | - ``mautic.cache.adapter.memcached`` 88 | 89 | .. code-block:: php 90 | 91 | 'memcached' => [ 92 | 'servers' => ['memcached://localhost'], 93 | 'options' => [ 94 | 'compression' => true, 95 | 'libketama_compatible' => true, 96 | 'serializer' => 'igbinary', 97 | ], 98 | ], 99 | 100 | - ``mautic.cache.adapter.redis`` 101 | 102 | Redis configuration in ``local.php``: 103 | 104 | .. code-block:: php 105 | 106 | 'redis' => [ 107 | 'dsn' => 'redis://localhost', 108 | 'options' => [ 109 | 'lazy' => false, 110 | 'persistent' => 0, 111 | 'persistent_id' => null, 112 | 'timeout' => 30, 113 | 'read_timeout' => 0, 114 | 'retry_interval' => 0, 115 | ], 116 | ], 117 | 118 | In order to use another adapter, just set it up as a service. 119 | 120 | Clearing the cache 121 | ------------------ 122 | 123 | The ``cache:clear`` command clears Mautic's cache. Use this command: 124 | 125 | .. code-block:: bash 126 | 127 | bin/console mautic:cache:clear 128 | 129 | -------------------------------------------------------------------------------- /docs/components/categories.rst: -------------------------------------------------------------------------------- 1 | Categories 2 | ########## 3 | 4 | Categories are a way to organize Mautic elements. 5 | Mautic has a ``CategoryBundle`` that you can leverage to incorporate Categories into your Plugin. 6 | 7 | .. vale off 8 | 9 | Adding Categories 10 | ----------------- 11 | 12 | .. vale on 13 | 14 | You can add Categories through your Plugin's ``config.php`` file by adding the following as a key to the returned config array: 15 | 16 | .. code-block:: php 17 | 18 | [ 21 | 'plugin:helloWorld' => 'mautic.helloworld.world.categories' 22 | ] 23 | 24 | Please prefix Category keys with ``plugin:`` as it determines permissions to manage Categories. 25 | The ``helloWorld`` should match the permission class name. 26 | 27 | .. vale off 28 | 29 | Configuring Categories for Routes 30 | --------------------------------- 31 | 32 | .. vale on 33 | 34 | There is no need to add custom routes for Categories, however, when generating a URL to the Plugin's Category list, use the following code: 35 | 36 | .. code-block:: php 37 | 38 | generateUrl('mautic_category_index', ['bundle' => 'plugin:helloWorld']); 41 | 42 | .. vale off 43 | 44 | Including Categories in Forms 45 | ----------------------------- 46 | 47 | .. vale on 48 | 49 | To add a Category select list to a Form, use ``category`` as the Form type and pass ``bundle`` as an option: 50 | 51 | .. code-block:: php 52 | 53 | add('category', 'category', [ 56 | 'bundle' => 'plugin:helloWorld' 57 | ]); 58 | 59 | .. vale off 60 | 61 | Restricting Category Management 62 | ------------------------------- 63 | 64 | .. vale on 65 | 66 | To restrict access to Categories, use the following in the Plugin's :ref:`Permission class`. 67 | 68 | In ``__construct()``, add ``$this->addStandardPermissions('categories');``, then in ``buildForm()``, add ``$this->addStandardFormFields('helloWorld', 'categories', $builder, $data);``. 69 | 70 | See a code example in :ref:`Roles and Permissions`. 71 | 72 | The two standard helper methods add the permissions of ``view``, ``edit``, ``create``, ``delete``, ``publish``, and ``full`` for Categories. 73 | -------------------------------------------------------------------------------- /docs/components/channels.rst: -------------------------------------------------------------------------------- 1 | Channels 2 | ######## 3 | 4 | Todo: 5 | 6 | .. vale off 7 | 8 | Listening for Channel features 9 | ------------------------------ 10 | 11 | .. vale on 12 | 13 | You can find several events through the ``ChannelEvents`` class. 14 | 15 | .. code-block:: php 16 | 17 | ['onAddChannel', 100], 23 | ]; 24 | } 25 | 26 | public function onAddChannel(ChannelEvent $event) 27 | { 28 | $event->addChannel( 29 | 'email', 30 | [ 31 | MessageModel::CHANNEL_FEATURE => [ 32 | 'campaignAction' => 'email.send', 33 | 'campaignDecisionsSupported' => [ 34 | 'email.open', 35 | 'page.pagehit', 36 | 'asset.download', 37 | 'form.submit', 38 | ], 39 | 'lookupFormType' => EmailListType::class, 40 | ], 41 | LeadModel::CHANNEL_FEATURE => [], 42 | ReportModel::CHANNEL_FEATURE => [ 43 | 'table' => 'emails', 44 | ], 45 | ] 46 | ); 47 | } 48 | 49 | Extending broadcasts 50 | -------------------- 51 | 52 | Broadcasts are communications sent in bulk through a Channel such as Email. 53 | An event is available to execute the sending of these bulk communications via the ``mautic:broadcasts:send`` command. 54 | 55 | To hook into the ``mautic:broadcasts:send`` command, create a listener for the ``\Mautic\CoreBundle\CoreEvents::CHANNEL_BROADCAST`` event. 56 | The event listener should check for the appropriate context and ID. 57 | 58 | .. code-block:: php 59 | 60 | model = $model; 79 | } 80 | 81 | public static function getSubscribedEvents(): array 82 | { 83 | return [ 84 | ChannelEvents::CHANNEL_BROADCAST => ['onChannelBroadcast', 0] 85 | ]; 86 | } 87 | 88 | public function onChannelBroadcast(ChannelBroadcastEvent $event): void 89 | { 90 | if (!$event->checkContext('world')) { 91 | return; 92 | } 93 | 94 | // Get list of published broadcasts or broadcast if there is only a single ID 95 | $id = $event->getId(); 96 | $broadcasts = $this->model->getRepository()->getPublishedBroadcasts($id); 97 | $output = $event->getOutput(); 98 | 99 | while (($broadcast = $broadcasts->next()) !== false) { 100 | list($sentCount, $failedCount, $ignore) = $this->model->sendIntergalacticMessages($broadcast[0], null, 100, true, $output); 101 | $event->setResults($this->translator->trans('plugin.helloworld').': '.$broadcast[0]->getName(), $sentCount, $failedCount); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /docs/components/config.rst: -------------------------------------------------------------------------------- 1 | Configuration 2 | ############# 3 | 4 | 5 | Configuration parameters 6 | ************************ -------------------------------------------------------------------------------- /docs/components/core.rst: -------------------------------------------------------------------------------- 1 | Core 2 | #### 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/components/images/test-connection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mautic/developer-documentation-new/bbfa70eea02830aadb2ec5a4dd8c28145291a1a8/docs/components/images/test-connection.png -------------------------------------------------------------------------------- /docs/components/integrations.rst: -------------------------------------------------------------------------------- 1 | Integrations 2 | ############ 3 | 4 | Each Integration provides its unique name as registered with Mautic, an icon, and a display name. When an Integration registers, the Integration helper classes manage the ``\Mautic\PluginBundle\Entity\Integration`` object through ``\Mautic\IntegrationsBundle\Integration\Interfaces\IntegrationInterface``. It handles decryption and encryption of the Integration's API keys, so the implementing code never has to. 5 | 6 | ---- 7 | 8 | .. vale off 9 | 10 | Registering the integration 11 | *************************** 12 | 13 | .. vale on 14 | 15 | All Integrations, whether using the config, auth, or sync interfaces, must have a class that registers itself with Mautic. The Integration should list on the ``/s/plugins`` UI route. 16 | 17 | In the Plugin's ``Config/config.php``, register the Integration using the tag ``mautic.basic_integration``. 18 | 19 | .. code-block:: php 20 | 21 | [ 25 | // ... 26 | 'integrations' => [ 27 | 'helloworld.integration' => [ 28 | 'class' => \MauticPlugin\HelloWorldBundle\Integration\HelloWorldIntegration::class, 29 | 'tags' => [ 30 | 'mautic.basic_integration', 31 | ], 32 | ], 33 | // ... 34 | ], 35 | // ... 36 | ], 37 | // ... 38 | ]; 39 | 40 | The ``HelloWorldIntegration`` needs to implement ``\Mautic\IntegrationsBundle\Integration\Interfaces\IntegrationInterface`` and ``\Mautic\IntegrationsBundle\Integration\Interfaces\BasicInterface`` interfaces. Most use cases can simply extend the ``\Mautic\IntegrationsBundle\Integration\BasicIntegration`` abstract class. 41 | 42 | .. php:class:: \Mautic\IntegrationsBundle\Integration\BasicIntegration 43 | 44 | .. php:method:: public function getName(): string; 45 | 46 | :return: Return the Integration's name. 47 | :returntype: string 48 | 49 | .. php:method:: public function getDisplayName(): string; 50 | 51 | :return: Return the Integration's display name. 52 | :returntype: string 53 | 54 | .. php:method:: public function getIcon(): string; 55 | 56 | :return: Get the path to the Integration's icon. 57 | :returntype: string 58 | 59 | 60 | 61 | .. code-block:: php 62 | 63 | ` as an Integration that requires authentication options. 97 | 2. Create a custom Symfony Form type for the required credentials and return it as part of the :ref:`config interface`. 98 | 3. Create a custom service that builds and configures the Guzzle client required to authenticate and communicate with the third party service. Use an :ref:`existing supported factory or create a new one`. 99 | 100 | Integration configuration 101 | ************************* 102 | 103 | 104 | If the Integration has extra configuration settings for features unique to it: 105 | 106 | 1. :ref:`Register the Integration` as an Integration that requires configuration options. 107 | 2. Create a custom Symfony Form type for the features and return it as part of the :ref:`Config Form feature setting interface`. 108 | 109 | .. vale off 110 | 111 | Integration sync engine 112 | *********************** 113 | 114 | .. vale on 115 | 116 | If the Integration syncs with Mautic's Contacts and/or Companies: 117 | 118 | 1. Read about :ref:`the sync engine`. 119 | 120 | .. vale off 121 | 122 | Integration Builders 123 | ******************** 124 | 125 | .. vale on 126 | 127 | If the Integration includes a Builder, Email, or Landing Page: 128 | 129 | 1. :ref:`Register the Integration` as an Integration that provides a custom builder. 130 | 2. Configure what featured builders the Integration supports (Mautic currently supports 'Email' and 'Landing Page' builders). 131 | -------------------------------------------------------------------------------- /docs/components/integrations_builder.rst: -------------------------------------------------------------------------------- 1 | .. It is a reference only page, not a part of doc tree. 2 | 3 | :orphan: 4 | 5 | .. vale off 6 | 7 | Builder integrations 8 | ##################### 9 | 10 | .. vale on 11 | 12 | Builders can register itself as a "builder" for Email and/or Landing Pages. 13 | 14 | ---- 15 | 16 | .. vale off 17 | 18 | Register the Integration as a Builder 19 | ************************************* 20 | 21 | .. vale on 22 | 23 | To tell the IntegrationsBundle that this Integration has configuration options, tag the Integration or support class with ``mautic.config_integration`` in the Plugin's ``app/config.php``. 24 | 25 | .. code-block:: php 26 | 27 | [ 31 | // ... 32 | 'integrations' => [ 33 | // ... 34 | 'helloworld.integration.builder' => [ 35 | 'class' => \MauticPlugin\HelloWorldBundle\Integration\Support\BuilderSupport::class, 36 | 'tags' => [ 37 | 'mautic.builder_integration', 38 | ], 39 | ], 40 | // ... 41 | ], 42 | // ... 43 | ], 44 | // ... 45 | ]; 46 | 47 | 48 | The ``BuilderSupport`` class must implement:: 49 | 50 | \Mautic\IntegrationsBundle\Integration\Interfaces\BuilderInterface 51 | 52 | The only method currently defined for the interface is ``isSupported`` which should return a boolean if it supports the given feature. Currently, Mautic supports ``email`` and ``page (Landing Pages)``. This determines what Themes should list as an option for the given builder/feature. 53 | -------------------------------------------------------------------------------- /docs/components/integrations_configuration_form_notes.rst: -------------------------------------------------------------------------------- 1 | .. It is a reference only page, not a part of doc tree. 2 | 3 | :orphan: 4 | 5 | .. vale off 6 | 7 | Integration configuration form notes 8 | #################################### 9 | 10 | .. vale on 11 | 12 | The Integration framework lets developer define their custom messages for the Plugin's configuration Form. 13 | 14 | The ``ConfigSupport`` class should implement the ``\Mautic\IntegrationsBundle\Integration\Interfaces\ConfigFormNotesInterface`` 15 | 16 | .. php:interface:: \Mautic\IntegrationsBundle\Integration\Interfaces\ConfigFormNotesInterface 17 | 18 | .. php:method:: public function getAuthorizationNote(): ?Note 19 | 20 | :return: The message and type for Auth tab. 21 | :returntype: :ref:`Note` 22 | 23 | .. php:method:: public function getFeaturesNote(): ?Note 24 | 25 | :return: The message and type for Features tab. 26 | :returntype: :ref:`Note` 27 | 28 | .. php:method:: public function getFieldMappingNote(): ?Note 29 | 30 | :return: The message and type for Field Mapping tab. 31 | :returntype: :ref:`Note` 32 | 33 | _____ 34 | 35 | .. vale off 36 | 37 | Note Object 38 | *********** 39 | 40 | .. vale:on 41 | 42 | .. php:class:: \Mautic\IntegrationsBundle\DTO\Note 43 | 44 | .. php:attr:: public note; 45 | 46 | .. php:attr:: public type; 47 | 48 | .. php:method:: public function getNote(): string 49 | 50 | :return: The string to display. 51 | :returntype: string 52 | 53 | .. php:method:: public function getType(): string 54 | 55 | :return: The note type, this helps annotate the note. 56 | :returntype: string 57 | 58 | The following code snippet shows the use of ``\Mautic\IntegrationsBundle\Integration\Interfaces\ConfigFormNotesInterface``. 59 | 60 | .. code-block:: php 61 | 62 | [ 37 | // ... 38 | 'integrations' => [ 39 | // ... 40 | 'helloworld.integration.sync' => [ 41 | 'class' => \MauticPlugin\HelloWorldBundle\Integration\Support\SyncSupport::class, 42 | 'tags' => [ 43 | 'mautic.sync_integration', 44 | ], 45 | ], 46 | // ... 47 | ], 48 | // ... 49 | ], 50 | // ... 51 | ]; 52 | 53 | The ``SyncSupport`` class must implement:: 54 | 55 | \Mautic\IntegrationsBundle\Integration\Interfaces\SyncInterface. 56 | 57 | Syncing 58 | ******* 59 | 60 | The mapping manual 61 | ================== 62 | 63 | The mapping manual tells the Sync Engine which Integration should sync with which Mautic object like Contact or Company, the Integration fields. These fields should get mapped to Mautic fields, and the direction in the data suppose to flow. 64 | 65 | See :xref:`MappingManualFactory`. 66 | 67 | The sync data exchange 68 | ====================== 69 | 70 | This is where the sync takes place, and the ``mautic:integrations:sync`` executes it. Mautic and the Integration build their respective Reports of new or modified objects, then execute the order from the other side. 71 | 72 | See :xref:`SyncDataExchange`. 73 | 74 | .. vale off 75 | 76 | Building Sync Report 77 | ____________________ 78 | 79 | .. vale on 80 | 81 | The Sync Report tells the Sync Engine what objects are new and/or modified between the two timestamps given by the engine. It's up to the Integration's discretion if it's a first-time sync. Objects should process in batches and ``RequestDAO::getSyncIteration()`` takes care of this batching. The Sync Engine executes ``SyncDataExchangeInterface::getSyncReport()`` until a Report comes back with no objects. 82 | 83 | If the Integration supports field level change tracking, it should tell the Report so that the Sync Engine can merge the two data sets more accurately. 84 | 85 | See :xref:`ReportBuilder`. 86 | 87 | .. vale off 88 | 89 | Executing the Sync Order 90 | ________________________ 91 | 92 | .. vale on 93 | 94 | The Sync Order contains all the changes the Sync Engine has determined, and these should inform the Integration. The Integration should communicate back the ID of any objects created or adjust objects as needed, such as if they get converted from one to another or deleted. 95 | 96 | See :xref:`OrderExecutioner`. 97 | -------------------------------------------------------------------------------- /docs/components/ip_lookups.rst: -------------------------------------------------------------------------------- 1 | IP lookup services 2 | ################## 3 | 4 | It's possible for your Plugin to retrieve the real User IP for the request. You can do so as follows: 5 | 6 | .. code-block:: php 7 | 8 | get('mautic.helper.ip_lookup'); 11 | 12 | $requestIp = $ipHelper->getIpAddressFromRequest(); // 1.2.3.4 13 | 14 | /** @var \Mautic\CoreBundle\Entity\IpAddress $ipAddressEntity */ 15 | $ipAddressEntity = $ipHelper->getIpAddress(); 16 | 17 | /** @var array $details */ 18 | $details = $ipAddressEntity->getIpDetails(); 19 | 20 | echo $details['city']; 21 | -------------------------------------------------------------------------------- /docs/components/landing_pages.rst: -------------------------------------------------------------------------------- 1 | Landing pages 2 | ############# 3 | 4 | There are two way to extend Landing Pages: 5 | - Landing Page tokens used to insert Dynamic Content into a Landing Page 6 | - A/B test winning criteria 7 | 8 | Both leverage the ``\Mautic\PageBundle\PageEvents::PAGE_ON_BUILD`` event. Read more about :ref:`plugins/event_listeners:Event listeners`. 9 | 10 | .. vale off 11 | 12 | Landing Page tokens 13 | ******************* 14 | 15 | .. vale on 16 | 17 | Landing Page tokens get handled exactly the same as :ref:`Email tokens`. 18 | 19 | .. vale off 20 | 21 | Page A/B Test Winner Criteria 22 | ***************************** 23 | 24 | Custom Landing Page A/B test winner criteria get handled exactly the same as :ref:`Email A/B test winner criteria` with the only differences being that the ``callback`` function gets passed ``Mautic\PageBundle\Entity\Page $page`` and ``Mautic\PageBundle\Entity\Page $parent`` instead. 25 | ``$children`` is an ArrayCollection of Page entities as well. 26 | 27 | .. vale on 28 | 29 | Below is an example of both Landing Page Tokens and Landing Page A/B Test Winner Criteria. 30 | 31 | .. code-block:: php 32 | 33 | templating = $templating; 53 | } 54 | 55 | static public function getSubscribedEvents() 56 | { 57 | return [ 58 | PageEvents::PAGE_ON_BUILD => ['onPageBuild', 0], 59 | PageEvents::PAGE_ON_DISPLAY => ['onPageDisplay', 0] 60 | ]; 61 | } 62 | 63 | /** 64 | * Register the tokens and a custom A/B test winner 65 | */ 66 | public function onPageBuild(PageBuilderEvent $event) 67 | { 68 | // Add page token 69 | $event->addToken('{helloworld.token}', 'Helloworld token'); 70 | 71 | // Add AB Test Winner Criteria 72 | $event->addAbTestWinnerCriteria( 73 | 'helloworld.planetvisits', 74 | array( 75 | // Label to group by 76 | 'group' => 'plugin.helloworld.header', 77 | 78 | // Label for this specific a/b test winning criteria 79 | 'label' => 'plugin.helloworld.pagetokens.', 80 | 81 | // Static callback function that will be used to determine the winner 82 | 'callback' => '\MauticPlugin\HelloWorldBundle\Helper\AbTestHelper::determinePlanetVisitWinner' 83 | ) 84 | ); 85 | } 86 | 87 | /** 88 | * Search and replace tokens with content 89 | */ 90 | public function onPageDisplay(PageDisplayEvent $event) 91 | { 92 | // Get content 93 | $content = $event->getContent(); 94 | 95 | // Search and replace tokens 96 | $content = str_replace( 97 | '{helloworld.token}', 98 | $this->templating->render('HelloWorldBundle:SubscribedEvents\PageToken:token.html.php');, 99 | $content 100 | ); 101 | 102 | // Set updated content 103 | $event->setContent($content); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /docs/components/maintenance.rst: -------------------------------------------------------------------------------- 1 | Extending maintenance cleanup 2 | ############################# 3 | 4 | To hook into the ``mautic:maintenance:cleanup`` command, create a listener for the ``\Mautic\CoreBundle\CoreEvents::MAINTENANCE_CLEANUP_DATA`` event. 5 | Use ``$event->setStat($key, $affectedRows, $sql, $sqlParameters)`` to give feedback to the CLI command. 6 | Note that ``$sql`` and ``$sqlParameters`` are only used for debugging and shown only in the ``dev`` environment. 7 | 8 | .. warning:: Don't delete records when the event is a dry run. You can use ``$event->isDryRun()`` to validate whether this is the case. See the code sample below for more details. 9 | 10 | .. code-block:: php 11 | 12 | db = $db; 33 | $this->translator = $translator; 34 | } 35 | 36 | public static function getSubscribedEvents() 37 | { 38 | return [ 39 | CoreEvents::MAINTENANCE_CLEANUP_DATA => ['onDataCleanup', -50] 40 | ]; 41 | } 42 | 43 | public function onDataCleanup(MaintenanceEvent $event) 44 | { 45 | $qb = $this->db->createQueryBuilder() 46 | ->setParameter('date', $event->getDate()->format('Y-m-d H:i:s')); 47 | 48 | if ($event->isDryRun()) { 49 | $rows = (int) $qb->select('count(*) as records') 50 | ->from(MAUTIC_TABLE_PREFIX . 'worlds', 'w') 51 | ->where( 52 | $qb->expr()->gte('w.date_added', ':date') 53 | ) 54 | ->execute() 55 | ->fetchColumn(); 56 | } else { 57 | $rows = (int) $qb->delete(MAUTIC_TABLE_PREFIX . 'worlds') 58 | ->where( 59 | $qb->expr()->lte('date_added', ':date') 60 | ) 61 | ->execute(); 62 | } 63 | 64 | $event->setStat($this->translator->trans('mautic.maintenance.hello_world'), $rows, $qb->getSQL(), $qb->getParameters()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /docs/components/queue.rst: -------------------------------------------------------------------------------- 1 | Queue 2 | ##### 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/components/reports.rst: -------------------------------------------------------------------------------- 1 | Reports 2 | ####### 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | # import os 14 | # import sys 15 | # sys.path.insert(0, os.path.abspath('.')) 16 | 17 | import sys, os 18 | import sphinx_rtd_theme 19 | 20 | sys.path.append(os.path.abspath('ext')) 21 | sys.path.append('.') 22 | 23 | from links.link import * 24 | from links import * 25 | 26 | # We get code samples directly from GitHub. For more info, see the README.md file of this repo. 27 | 28 | from code_samples._main_code_sample import * 29 | from code_samples import * 30 | 31 | # -- Project information ----------------------------------------------------- 32 | 33 | project = 'Mautic Developer Documentation' 34 | copyright = '2021, Mautic contributors' 35 | author = 'Mautic contributors' 36 | 37 | # The full version, including alpha/beta/rc tags 38 | release = '3.0.0' 39 | 40 | 41 | # -- General configuration --------------------------------------------------- 42 | 43 | # Add any Sphinx extension module names here, as strings. They can be 44 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 45 | # ones. 46 | extensions = [ 47 | 'xref', 48 | 'phpdomain', 49 | 'code_samples_ext', 50 | 'sphinx_rtd_theme', 51 | 'sphinx.ext.viewcode', 52 | 'sphinx.ext.autosectionlabel', 53 | ] 54 | 55 | # Add any paths that contain templates here, relative to this directory. 56 | #html_theme_path = ['_templates'] 57 | 58 | # List of patterns, relative to source directory, that match files and 59 | # directories to ignore when looking for source files. 60 | # This pattern also affects html_static_path and html_extra_path. 61 | exclude_patterns = [] 62 | 63 | templates_path = ['_templates'] 64 | 65 | # -- Options for HTML output ------------------------------------------------- 66 | 67 | # The theme to use for HTML and HTML Help pages. See the documentation for 68 | # a list of builtin themes. 69 | # 70 | html_theme = 'sphinx_rtd_theme' 71 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 72 | 73 | # Add any paths that contain custom static files (such as style sheets) here, 74 | # relative to this directory. They are copied after the builtin static files, 75 | # so a file named "default.css" will overwrite the builtin "default.css". 76 | html_static_path = ['_static'] 77 | 78 | # Please add links here that do not pass the "make checklinks" check. 79 | # A little context on the reason for ignoring is greatly appreciated! 80 | linkcheck_ignore = [ 81 | # github anchors cause failures, so do not check any github url with an anchor 82 | r'^https://github.com/.*#.*' 83 | ] 84 | 85 | # Ensure that autosectionlabel will produce unique names 86 | autosectionlabel_prefix_document = True 87 | # autosectionlabel_maxdepth = 1 88 | -------------------------------------------------------------------------------- /docs/design/accordion.rst: -------------------------------------------------------------------------------- 1 | Accordion component 2 | ################### 3 | 4 | Introduction 5 | ************ 6 | 7 | The Accordion Component allows developers to create collapsible sections within their Mautic templates. This Component is useful for organizing content into expandable and collapsible panels, enhancing the User experience by making large amounts of content more manageable. 8 | 9 | 10 | Template structure 11 | ****************** 12 | 13 | The ``accordion.html.twig`` template defines this accordion component. The template employs a list structure ``