├── .github ├── styles │ ├── Google │ │ ├── vocab.txt │ │ ├── meta.json │ │ ├── Will.yml │ │ ├── Colons.yml │ │ ├── Parens.yml │ │ ├── Ordinal.yml │ │ ├── Semicolons.yml │ │ ├── Exclamation.yml │ │ ├── OxfordComma.yml │ │ ├── Quotes.yml │ │ ├── Spacing.yml │ │ ├── Ellipses.yml │ │ ├── Periods.yml │ │ ├── Spelling.yml │ │ ├── Ranges.yml │ │ ├── Gender.yml │ │ ├── AMPM.yml │ │ ├── Units.yml │ │ ├── Slang.yml │ │ ├── EmDash.yml │ │ ├── EnDash.yml │ │ ├── We.yml │ │ ├── Latin.yml │ │ ├── LyHyphens.yml │ │ ├── OptionalPlurals.yml │ │ ├── FirstPerson.yml │ │ ├── HeadingPunctuation.yml │ │ ├── DateFormat.yml │ │ ├── Headings.yml │ │ ├── Contractions.yml │ │ ├── Acronyms.yml │ │ ├── GenderBias.yml │ │ ├── WordList.yml │ │ └── Passive.yml │ ├── Mautic │ │ └── FeatureList.yml │ └── config │ │ └── vocabularies │ │ └── Mautic │ │ └── accept.txt ├── dependabot.yml ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── 02_feature_request.yml │ ├── 01_documentation_fix.yml │ └── 03_bug_report.yml ├── holopin.yml ├── pull_request_template.md └── workflows │ └── linting.yml ├── docs ├── code_samples_downloaded │ └── .gitkeep ├── components │ ├── core.rst │ ├── queue.rst │ ├── reports.rst │ ├── config.rst │ ├── images │ │ └── test-connection.png │ ├── ip_lookups.rst │ ├── integrations_builder.rst │ ├── categories.rst │ ├── maintenance.rst │ ├── api.rst │ ├── channels.rst │ ├── landing_pages.rst │ ├── integrations_sync.rst │ ├── integrations_configuration_form_notes.rst │ └── cache.rst ├── links │ ├── link.py │ ├── __init__.py │ ├── phpunit.py │ ├── rector.py │ ├── phpstan.py │ ├── codecov.py │ ├── composer.py │ ├── packagist.py │ ├── doctrine.py │ ├── mautic_slack.py │ ├── mjml.py │ ├── request_bin.py │ ├── legacy_docs.py │ ├── mautic_api_library.py │ ├── symfony_framework.py │ ├── twig_docs.py │ ├── codecov_gh_app.py │ ├── github_actions.py │ ├── mautic_tester_docs.py │ ├── grapesjs_api.py │ ├── mautic_dev_forum.py │ ├── php_cs_fixer.py │ ├── semantic_versioning_website.py │ ├── mautic_open_collective.py │ ├── phpstan_baseline.py │ ├── grapesjs_plugins.py │ ├── mautic_contribution_docs.py │ ├── mautic_github.py │ ├── plugin_helloworld.py │ ├── symfony_docs.py │ ├── twig_lint.py │ ├── hello_world_plugin.py │ ├── mautic_dev_portal.py │ ├── composer_plugin.py │ ├── mautic_end_user_docs.py │ ├── ddev_install.py │ ├── mautic_become_a_member.py │ ├── mautic_recommended_project.py │ ├── mautic_upgrade_guide.py │ ├── symfony_autowiring.py │ ├── ab_testing.py │ ├── doctrine_docs_orm.py │ ├── grapesjs_demo_plugin.py │ ├── symfony_best_practices.py │ ├── builder_docs.py │ ├── contributing_to_mautic.py │ ├── mautic_code_governance.py │ ├── symfony_cache_component.py │ ├── symfony_docs_service_tags.py │ ├── translating_mautic.py │ ├── mautic_portal_roadmap.py │ ├── symfony_docs_form_classes.py │ ├── symfony_docs_service_factories.py │ ├── webhook_cronjob.py │ ├── mautic_portal_product_team.py │ ├── mautic_project_governance.py │ ├── symfony_autoconfigure.py │ ├── symfony_docs_constraints.py │ ├── symfony_docs_form_validation.py │ ├── default_services_config.py │ ├── mautic_supported_php_versions.py │ ├── symfony_coding_standards.py │ ├── action_based_di.py │ ├── invokable_controllers.py │ ├── manually_wiring_arguments.py │ ├── mautic_devdocs_issues.py │ ├── mautic_docs_mtc_js.py │ ├── php_version_compare.py │ ├── mautic_php_to_twig.py │ ├── plugin_integration_guzzle_oauth2_subscriber.py │ ├── marketplace_allowlist_application_form.py │ ├── symfony_tag_console_command.py │ ├── doctrine_docs_migrations_bundle.py │ ├── symfony_docs_form_validation_groups.py │ ├── symfony_docs_service_decoration.py │ ├── symfony_tag_subscriber.py │ ├── symfony_docs_service_configurators.py │ ├── symfony_docs_service_sythetic_services.py │ ├── symfony_docs_configuration_environments.py │ ├── symfony_tag_custom_form_field_type.py │ ├── mautic_dev_contribution_guide.py │ ├── symfony_docs_dynamic_form_modification.py │ ├── symfony_docs_event_subscribers.py │ ├── doctrine_docs_orm_annotations.py │ ├── symfony_tag_controller_service_arguments.py │ ├── doctrine_docs_orm_php_mapping.py │ ├── excluded_by_default_autowiring.py │ ├── symfony_docs_custom_form_fields_type.py │ ├── doctrine_docs_orm_query_builder.py │ ├── plugin_helloworld_report_builder.py │ ├── plugin_helloworld_order_executioner.py │ ├── plugin_helloworld_sync_data_exchange.py │ ├── plugin_helloworld_mapping_manunal.py │ ├── symfony_docs_services_requiring_file_before_loading.py │ ├── plugin_integration_config_form_callback_interface.py │ └── symfony_docs_best_pratice_environments.py ├── plugins │ ├── event_listeners.rst │ ├── dependencies.rst │ ├── translations.rst │ ├── getting_started.rst │ ├── structure.rst │ └── installation.rst ├── design │ ├── images │ │ ├── tile.png │ │ └── tile-clickable.png │ ├── availability.rst │ ├── protip.rst │ ├── retrieving_system_information.rst │ ├── tiles.rst │ ├── quick_filters.rst │ └── accordion.rst ├── _templates │ └── layout.html ├── code_samples │ ├── _main_code_sample.py │ ├── __init__.py │ └── helloworld_entity_world.py ├── testing │ └── images │ │ └── e2e_test_suite.png ├── _static │ ├── theme.css │ └── tablefix.css ├── requirements.txt ├── development-environment │ ├── getting_started.rst │ ├── how_to_install_with_ddev.rst │ ├── setup.rst │ └── environments.rst ├── webhooks │ ├── events │ │ ├── lead_post_save_update.rst │ │ ├── email_on_open.rst │ │ ├── index.rst │ │ ├── lead_post_delete.rst │ │ ├── sms_on_send.rst │ │ ├── company_post_delete.rst │ │ ├── lead_company_change.rst │ │ ├── lead_points_change.rst │ │ ├── lead_channel_subscription_changed.rst │ │ ├── form_on_submit.rst │ │ ├── company_post_save.rst │ │ └── page_on_hit.rst │ └── example_scripts.rst ├── themes │ ├── system.rst │ ├── forms.rst │ └── legacy.rst ├── make.bat ├── marketplace │ ├── getting_started.rst │ ├── best_practices.rst │ ├── allowlist_what_and_why.rst │ └── listing.rst ├── ext │ ├── xref.py │ └── code_samples_ext.py ├── form_hooks │ ├── getting_started.rst │ ├── general_hooks.rst │ ├── response_hooks.rst │ └── validation_hooks.rst ├── Makefile └── conf.py ├── .well-known └── funding-manifest-urls ├── .gitignore ├── .vale.ini ├── .ddev ├── config.yaml ├── web-build │ └── Dockerfile.sphinx └── commands │ └── web │ └── build-docs ├── .vscode ├── extensions.json ├── welcome.md └── settings.json ├── .gitpod.yml ├── .devcontainer ├── setup-project.sh └── devcontainer.json ├── LICENSE.txt ├── .readthedocs.yaml ├── .gitpod.Dockerfile └── .all-contributorsrc /.github/styles/Google/vocab.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/code_samples_downloaded/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/components/core.rst: -------------------------------------------------------------------------------- 1 | Core 2 | #### 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/components/queue.rst: -------------------------------------------------------------------------------- 1 | Queue 2 | ##### 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/components/reports.rst: -------------------------------------------------------------------------------- 1 | Reports 2 | ####### 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/links/link.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | xref_links = {"key" : ("link text", "URL")} -------------------------------------------------------------------------------- /docs/plugins/event_listeners.rst: -------------------------------------------------------------------------------- 1 | Event listeners 2 | =============== 3 | -------------------------------------------------------------------------------- /.well-known/funding-manifest-urls: -------------------------------------------------------------------------------- 1 | https://github.com/mautic/mautic/blob/5.x/funding.json 2 | -------------------------------------------------------------------------------- /docs/components/config.rst: -------------------------------------------------------------------------------- 1 | Configuration 2 | ############# 3 | 4 | 5 | Configuration parameters 6 | ************************ -------------------------------------------------------------------------------- /docs/design/images/tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mautic/developer-documentation-new/HEAD/docs/design/images/tile.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .idea 3 | __pycache__ 4 | .DS_Store 5 | docs/code_samples_downloaded/* 6 | !docs/code_samples_downloaded/.gitkeep 7 | -------------------------------------------------------------------------------- /.github/styles/Google/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "feed": "https://github.com/errata-ai/Google/releases.atom", 3 | "vale_version": ">=1.0.0" 4 | } 5 | -------------------------------------------------------------------------------- /docs/_templates/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "!layout.html" %} 2 | {% set css_files = css_files + ["_static/tablefix.css"] + ["_static/theme.css"] %} -------------------------------------------------------------------------------- /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/design/images/tile-clickable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mautic/developer-documentation-new/HEAD/docs/design/images/tile-clickable.png -------------------------------------------------------------------------------- /docs/testing/images/e2e_test_suite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mautic/developer-documentation-new/HEAD/docs/testing/images/e2e_test_suite.png -------------------------------------------------------------------------------- /.vale.ini: -------------------------------------------------------------------------------- 1 | StylesPath = .github/styles 2 | Vocab = Mautic 3 | MinAlertLevel = suggestion 4 | [*.{md,rst}] 5 | BasedOnStyles = Vale, Google, Mautic 6 | -------------------------------------------------------------------------------- /docs/components/images/test-connection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mautic/developer-documentation-new/HEAD/docs/components/images/test-connection.png -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.ddev/config.yaml: -------------------------------------------------------------------------------- 1 | type: generic 2 | docroot: "docs/build/html" 3 | working_dir: 4 | web: /var/www/html/docs 5 | omit_containers: [db] 6 | hooks: 7 | post-start: 8 | - exec: "make html" 9 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /docs/links/__init__.py: -------------------------------------------------------------------------------- 1 | from os.path import dirname, basename, isfile 2 | 3 | import glob 4 | modules = glob.glob(dirname(__file__)+"/*.py") 5 | 6 | __all__ = [ basename(f)[:-3] for f in modules if isfile(f)] -------------------------------------------------------------------------------- /docs/links/phpunit.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "phpunit" 4 | link_text = "PHPUnit" 5 | link_url = "https://phpunit.de" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/rector.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "rector" 4 | link_text = "Rector" 5 | link_url = "https://getrector.com" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/phpstan.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "phpstan" 4 | link_text = "PHPStan" 5 | link_url = "https://phpstan.org" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/codecov.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "codecov" 4 | link_text = "Codecov" 5 | link_url = "https://about.codecov.io" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/composer.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "composer" 4 | link_text = "Composer" 5 | link_url = "https://getcomposer.org" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/packagist.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Packagist" 4 | link_text = "Packagist" 5 | link_url = "https://packagist.org" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /.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/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 | -------------------------------------------------------------------------------- /docs/links/doctrine.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Doctrine" 4 | link_text = "Doctrine" 5 | link_url = "https://www.doctrine-project.org/" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/mautic_slack.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Mautic Slack" 4 | link_text = "Slack" 5 | link_url = "https://mau.tc/slack-invite" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/mjml.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "MJML email framework" 4 | link_text = "MJML email framework" 5 | link_url = "https://mjml.io" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/request_bin.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "RequestBin" 4 | link_text = "RequestBin" 5 | link_url = "https://pipedream.com/requestbin" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/legacy_docs.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Legacy Developer Docs" 4 | user_text = "Legacy Developer Documentation" 5 | url = "https://developer.mautic.org" 6 | 7 | link.xref_links.update({link_name: (user_text, url)}) -------------------------------------------------------------------------------- /docs/links/mautic_api_library.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Mautic API Library" 4 | user_text = "Mautic API Library" 5 | url = "https://github.com/mautic/api-library" 6 | 7 | link.xref_links.update({link_name: (user_text, url)}) -------------------------------------------------------------------------------- /docs/links/symfony_framework.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Symfony framework" 4 | link_text = "Symfony framework" 5 | link_url = "https://symfony.com/" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/twig_docs.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Twig documentation" 4 | link_text = "Twig templating language" 5 | link_url = "https://twig.symfony.com" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # For code owners docs, see: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners 2 | 3 | # Global owners 4 | 5 | * @mautic/education-team-leaders -------------------------------------------------------------------------------- /.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/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 | -------------------------------------------------------------------------------- /docs/links/codecov_gh_app.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "codecov_gh_app" 4 | link_text = "Codecov GitHub App" 5 | link_url = "https://github.com/apps/codecov" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/github_actions.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "GitHub Actions" 4 | link_text = "GitHub Actions" 5 | link_url = "https://github.com/features/actions" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/mautic_tester_docs.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Mautic Tester Docs" 4 | link_text = "Tester Documentation" 5 | link_url = "https://mau.tc/tester" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /.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/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/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 | -------------------------------------------------------------------------------- /docs/links/grapesjs_api.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "grapesjs-api" 4 | link_text = "GrapesJS API documentation" 5 | link_url = "https://grapesjs.com/docs/api/" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/mautic_dev_forum.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Developer Forum" 4 | link_text = "Developer Forum" 5 | link_url = "https://forum.mautic.org/c/development" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/php_cs_fixer.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "php_cs_fixer" 4 | link_text = "PHP CS Fixer" 5 | link_url = "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/semantic_versioning_website.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Semantic versioning" 4 | link_text = "Semantic versioning" 5 | link_url = "https://semver.org" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/mautic_open_collective.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Open Collective" 4 | link_text = "Open Collective" 5 | link_url = "https://opencollective.com/mautic" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/phpstan_baseline.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "PHPSTAN baseline" 4 | link_text = "PHPSTAN baseline" 5 | link_url = "https://phpstan.org/user-guide/baseline" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /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/links/grapesjs_plugins.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "grapesjs-plugins" 4 | link_text = "GrapesJS Plugins" 5 | link_url = "https://grapesjs.com/docs/modules/Plugins.html" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/mautic_contribution_docs.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Contribute to Mautic" 4 | link_text = "Contributing to Mautic" 5 | link_url = "https://mau.tc/contribute" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/mautic_github.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Mautic's GitHub Repo" 4 | link_text = "Mautic's GitHub repository" 5 | link_url = "https://github.com/mautic/mautic" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/plugin_helloworld.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Plugin HelloWorld" 4 | link_text = "Plugin HelloWorld" 5 | link_url = "https://github.com/mautic/plugin-helloworld" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/symfony_docs.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Symfony documentation" 4 | link_text = "Symfony 5 documentation" 5 | link_url = "https://symfony.com/doc/5.x/index.html" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/twig_lint.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "twig_lint" 4 | link_text = "Twig Lint" 5 | link_url = "https://symfony.com/doc/5.x/templates.html#linting-twig-templates" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /docs/links/hello_world_plugin.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "hello_world_plugin" 4 | link_text = "Hello World Plugin" 5 | link_url = "https://github.com/mautic/plugin-helloworld" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/mautic_dev_portal.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Mautic Developer Portal" 4 | link_text = "https://developer.mautic.org" 5 | link_url = "https://developer.mautic.org" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /.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/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 | -------------------------------------------------------------------------------- /docs/links/composer_plugin.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Mautic's Composer plugin" 4 | link_text = "Mautic's Composer plugin" 5 | link_url = "https://github.com/mautic/composer-plugin" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/mautic_end_user_docs.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Mautic End User Docs" 4 | link_text = "end-user documentation" 5 | link_url = "https://docs.mautic.org/en/5.x/index.html" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/ddev_install.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "ddev install" 4 | link_text = "Install DDEV" 5 | link_url = "https://ddev.readthedocs.io/en/stable/users/install/ddev-installation/" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/mautic_become_a_member.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Become a Member of Mautic" 4 | link_text = "Member of Mautic" 5 | link_url = "https://mautic.org/become-a-member-of-mautic" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/mautic_recommended_project.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Recommended Project" 4 | link_text = "Recommended Project" 5 | link_url = "https://github.com/mautic/recommended-project" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/mautic_upgrade_guide.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "UPGRADE_GUIDE" 4 | link_text = "UPGRADE-5.0.md guide" 5 | link_url = "https://github.com/mautic/mautic/blob/5.x/UPGRADE-5.0.md" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/symfony_autowiring.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "symfony-autowiring" 4 | link_text = "autowiring" 5 | link_url = "https://symfony.com/doc/5.x/service_container/autowiring.html" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /docs/links/ab_testing.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "A/B testing" 4 | link_text = "A/B testing" 5 | link_url = "https://kb.mautic.org/article/how-to-create-an-a-b-test-for-emails-in-mautic.html" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/doctrine_docs_orm.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Doctrine ORM" 4 | link_text = "Doctrine ORM" 5 | link_url = "https://www.doctrine-project.org/projects/doctrine-orm/en/2.14/index.html" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/grapesjs_demo_plugin.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "GrapesJS Demo Plugin" 4 | link_text = "demo GrapesJS Plugin" 5 | link_url = "https://github.com/mautic/GrapesJsCustomPluginBundle" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/symfony_best_practices.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Symfony best practices" 4 | link_text = "Symfony best practices" 5 | link_url = "https://symfony.com/doc/5.x/best_practices.html" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/builder_docs.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Builder documentation" 4 | link_text = "Builder documentation" 5 | link_url = "https://docs.mautic.org/en/5.x/builders/email_landing_page.html" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/contributing_to_mautic.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Contributing to Mautic" 4 | link_text = "Contributing to Mautic" 5 | link_url = "https://contribute.mautic.org/contributing-to-mautic" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/mautic_code_governance.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Mautic Code Governance" 4 | link_text = "code governance" 5 | link_url = "https://contribute.mautic.org/governance/code-governance" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/symfony_cache_component.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Symfony Cache Component" 4 | link_text = "Symfony Cache Component" 5 | link_url = "https://symfony.com/doc/current/components/cache.html" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) -------------------------------------------------------------------------------- /docs/links/symfony_docs_service_tags.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Symfony service tags" 4 | link_text = "Symfony 5 service tags" 5 | link_url = "https://symfony.com/doc/5.x/service_container/tags.html" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/translating_mautic.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Translating Mautic" 4 | link_text = "Translating Mautic" 5 | link_url = "https://contribute.mautic.org/contributing-to-mautic/translator" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 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 | -------------------------------------------------------------------------------- /docs/links/mautic_portal_roadmap.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Community Portal Roadmap" 4 | link_text = "proposals for new major features" 5 | link_url = "https://community.mautic.org/processes/roadmap" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/symfony_docs_form_classes.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Symfony form classes" 4 | link_text = "Symfony 5 form classes" 5 | link_url = "https://symfony.com/doc/5.x/forms.html#creating-form-classes" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/symfony_docs_service_factories.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Symfony factories" 4 | link_text = "Symfony 5 factories" 5 | link_url = "https://symfony.com/doc/5.x/service_container/factories.html" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/webhook_cronjob.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Webhook Cron Job" 4 | link_text = "Webhook Cron Job" 5 | link_url = "https://docs.mautic.org/en/5.x/configuration/cron_jobs.html#webhooks-cron-job" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/mautic_portal_product_team.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Community Portal Product Team" 4 | link_text = "Product Team Assembly" 5 | link_url = "https://community.mautic.org/assemblies/product-team" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/mautic_project_governance.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Mautic Project Governance" 4 | link_text = "Mautic Project Governance" 5 | link_url = "https://contribute.mautic.org/governance/governance" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/symfony_autoconfigure.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "symfony-autoconfigure" 4 | link_text = "autoconfigure" 5 | link_url = "https://symfony.com/doc/5.x/service_container.html#the-autoconfigure-option" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/symfony_docs_constraints.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Symfony Form constraints" 4 | link_text = "Symfony 5 Form constraints" 5 | link_url = "https://symfony.com/doc/5.x/reference/constraints.html" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/symfony_docs_form_validation.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Symfony form validation" 4 | link_text = "Symfony 5 form validation" 5 | link_url = "https://symfony.com/doc/5.x/forms.html#validating-forms" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /docs/links/default_services_config.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "default_services_config" 4 | link_text = "default configuration" 5 | link_url = "https://github.com/mautic/mautic/blob/HEAD/app/config/services.php" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/mautic_supported_php_versions.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Mautic supported PHP versions" 4 | link_text = "Mautic supported PHP versions" 5 | link_url = "https://www.mautic.org/download/requirements" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/symfony_coding_standards.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Symfony coding standards" 4 | link_text = "Symfony coding standards" 5 | link_url = "https://symfony.com/doc/5.x/contributing/code/standards.html" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /.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/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 | -------------------------------------------------------------------------------- /docs/links/action_based_di.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "action_based_di" 4 | link_text = "action-based dependency injection" 5 | link_url = "https://symfony.com/doc/5.x/controller.html#controller-accessing-services" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/invokable_controllers.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "invokable_controllers" 4 | link_text = "invokable controllers" 5 | link_url = "https://symfony.com/doc/5.x/controller/service.html#invokable-controllers" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/manually_wiring_arguments.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "manually_wiring_arguments" 4 | link_text = "Symfony docs" 5 | link_url = "https://symfony.com/doc/5.x/service_container.html#manually-wiring-arguments" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/mautic_devdocs_issues.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Mautic Dev Docs Issues" 4 | link_text = "developer documentation issue queue" 5 | link_url = "https://github.com/mautic/developer-documentation-new/issues" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/mautic_docs_mtc_js.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Mautic tracking script docs" 4 | link_text = "Mautic tracking script docs" 5 | link_url = "https://docs.mautic.org/en/5.x/configuration/tracking_script.html" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/php_version_compare.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "PHP standardized version strings" 4 | link_text = "PHP standardized version strings" 5 | link_url = "http://php.net/manual/en/function.version-compare.php" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /docs/links/mautic_php_to_twig.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "PHP to Twig migration" 4 | link_text = "Upgrading PHP to Twig templates" 5 | link_url = "https://github.com/mautic/mautic/blob/5.x/UPGRADE-PHP-TO-TWIG-TEMPLATES.md" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/plugin_integration_guzzle_oauth2_subscriber.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Guzzle Oauth2 Subscriber" 4 | link_text = "Guzzle Oauth2 Subscriber" 5 | link_url = "https://github.com/kamermans/guzzle-oauth2-subscriber" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/marketplace_allowlist_application_form.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Marketplace allowlist application form" 4 | link_text = "Marketplace allowlist application form" 5 | link_url = "https://mau.tc/marketplace-allowlist" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/symfony_tag_console_command.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Symfony console command tag" 4 | link_text = "Symfony 5 console command tag" 5 | link_url = "https://symfony.com/doc/5.x/reference/dic_tags.html#console-command" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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/links/doctrine_docs_migrations_bundle.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Doctrine migrations bundle" 4 | link_text = "Doctrine migrations bundle" 5 | link_url = "https://symfony.com/bundles/DoctrineMigrationsBundle/2.2.x/index.html" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/symfony_docs_form_validation_groups.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Symfony 5 Form validation groups" 4 | link_text = "Symfony 5 Form validation groups" 5 | link_url = "https://symfony.com/doc/5.x/form/validation_groups.html" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/symfony_docs_service_decoration.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Symfony service decoration" 4 | link_text = "Symfony 5 service decoration" 5 | link_url = "https://symfony.com/doc/5.x/service_container/service_decoration.html" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/symfony_tag_subscriber.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Symfony event subscriber tag" 4 | link_text = "Symfony 5 event subscriber tag" 5 | link_url = "https://symfony.com/doc/5.x/reference/dic_tags.html#kernel-event-subscriber" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /.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/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 | -------------------------------------------------------------------------------- /docs/links/symfony_docs_service_configurators.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Symfony service configurators" 4 | link_text = "Symfony 5 service configurators" 5 | link_url = "https://symfony.com/doc/5.x/service_container/configurators.html" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/symfony_docs_service_sythetic_services.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Symfony synthetic services" 4 | link_text = "Symfony 5 synthetic services" 5 | link_url = "https://symfony.com/doc/5.x/service_container/synthetic_services.html" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/symfony_docs_configuration_environments.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Symfony environment conventions" 4 | link_text = "Mautic follows the Symfony 5.5 environment conventions:" 5 | link_url = "https://symfony.com/doc/5.x/index.html" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/symfony_tag_custom_form_field_type.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Symfony custom form field type tag" 4 | link_text = "Symfony 5 custom form field type tag" 5 | link_url = "https://symfony.com/doc/5.x/reference/dic_tags.html#form-type" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/mautic_dev_contribution_guide.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Mautic Developer Contribution Guide" 4 | link_text = "Mautic Developer Contribution Guide" 5 | link_url = "https://contribute.mautic.org/en/latest/contributing/developer.html" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/symfony_docs_dynamic_form_modification.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Symfony dynamic form modification" 4 | link_text = "Symfony 5 dynamic form modification" 5 | link_url = "https://symfony.com/doc/5.x/form/dynamic_form_modification.html" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/symfony_docs_event_subscribers.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "symfony-event-subscribers" 4 | link_text = "Symfony's documentation" 5 | link_url = "https://symfony.com/doc/current/components/event_dispatcher.html#using-event-subscribers" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | 9 | -------------------------------------------------------------------------------- /docs/links/doctrine_docs_orm_annotations.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Doctrine ORM annotations" 4 | link_text = "Doctrine ORM annotations" 5 | link_url = "https://www.doctrine-project.org/projects/doctrine-orm/en/2.14/reference/annotations-reference.html" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/symfony_tag_controller_service_arguments.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Symfony controller service arguments tag" 4 | link_text = "Symfony 5 controller service arguments tag" 5 | link_url = "https://symfony.com/doc/5.x/controller/service.html" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /docs/links/doctrine_docs_orm_php_mapping.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Doctrine ORM PHP mapping" 4 | link_text = "Doctrine ORM PHP mapping" 5 | link_url = "https://www.doctrine-project.org/projects/doctrine-orm/en/2.14/reference/php-mapping.html#classmetadatabuilder" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/excluded_by_default_autowiring.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "excluded_directories" 4 | link_text = "excluded by default" 5 | link_url = "https://github.com/mautic/mautic/blob/HEAD/app/bundles/CoreBundle/DependencyInjection/MauticCoreExtension.php#L12" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/symfony_docs_custom_form_fields_type.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Symfony custom Form field type" 4 | link_text = "Symfony 5 custom Form field type" 5 | link_url = "https://symfony.com/doc/5.x/form/create_custom_field_type.html#defining-the-form-type" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /docs/links/doctrine_docs_orm_query_builder.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Doctrine ORM Query Builder" 4 | link_text = "Doctrine ORM Query Builder" 5 | link_url = "https://www.doctrine-project.org/projects/doctrine-orm/en/2.13/reference/query-builder.html#the-expr-class" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/plugin_helloworld_report_builder.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "ReportBuilder" 4 | link_text = "HelloWorldBundle/Sync/DataExchange/ReportBuilder.php" 5 | link_url = "https://github.com/mautic/plugin-helloworld/blob/mautic-4/Sync/DataExchange/ReportBuilder.php" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | # File: docs/requirements.txt 2 | 3 | # Defining the exact version will make sure things don't break 4 | sphinx==8.0.2 5 | sphinx_rtd_theme==3.0.0 6 | readthedocs-sphinx-search==0.3.2 7 | rstcheck==6.2.4 8 | myst-parser==4.0.0 9 | linkify-it-py==2.0.3 10 | esbonio==0.16.5 11 | attrs==24.2.0 12 | sphinxcontrib-phpdomain==0.13.2 13 | -------------------------------------------------------------------------------- /docs/links/plugin_helloworld_order_executioner.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "OrderExecutioner" 4 | link_text = "HelloWorldBundle/Sync/DataExchange/OrderExecutioner.php" 5 | link_url = "https://github.com/mautic/plugin-helloworld/blob/mautic-4/Sync/DataExchange/OrderExecutioner.php" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/plugin_helloworld_sync_data_exchange.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "SyncDataExchange" 4 | link_text = "HelloWorldBundle/Sync/DataExchange/SyncDataExchange.php" 5 | link_url = "https://github.com/mautic/plugin-helloworld/blob/mautic-4/Sync/DataExchange/SyncDataExchange.php" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | contact_links: 2 | - name: Mautic Community Support 3 | url: https://forum.mautic.org/ 4 | about: Please ask questions around Mautic product here. 5 | - name: Report a bug for the Mautic product 6 | url: https://github.com/mautic/mautic/issues 7 | about: Please create an issue to report a bug in the Mautic product here. -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /docs/links/plugin_helloworld_mapping_manunal.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "MappingManualFactory" 4 | link_text = "HelloWorldBundle/Sync/Mapping/Manual/MappingManualFactory.php" 5 | link_url = "https://github.com/mautic/plugin-helloworld/blob/mautic-4/Sync/Mapping/Manual/MappingManualFactory.php" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/symfony_docs_services_requiring_file_before_loading.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Symfony requiring a file before loading a service" 4 | link_text = "Symfony 5 requiring a file before loading a service" 5 | link_url = "https://symfony.com/doc/5.x/service_container/definitions.html#requiring-files" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /.ddev/web-build/Dockerfile.sphinx: -------------------------------------------------------------------------------- 1 | RUN apt update || true 2 | RUN apt install -y build-essential python3.11-venv 3 | RUN apt remove pipx 4 | RUN curl -sSL https://github.com/pypa/pipx/releases/latest/download/pipx.pyz -o /usr/local/bin/pipx 5 | RUN chmod +x /usr/local/bin/pipx 6 | 7 | RUN pipx install --global sphinx 8 | RUN pipx inject --global sphinx sphinxcontrib-phpdomain sphinx-rtd-theme 9 | -------------------------------------------------------------------------------- /docs/links/plugin_integration_config_form_callback_interface.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "ConfigFormCallbackInterface" 4 | link_text = "ConfigFormCallbackInterface" 5 | link_url = "https://github.com/mautic/mautic/blob/5.x/app/bundles/IntegrationsBundle/Integration/Interfaces/ConfigFormCallbackInterface.php" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /docs/links/symfony_docs_best_pratice_environments.py: -------------------------------------------------------------------------------- 1 | from . import link 2 | 3 | link_name = "Symfony best practices environment variables" 4 | link_text = "Symfony 5 best practices for environment variables" 5 | link_url = "https://symfony.com/doc/5.x/best_practices.html#use-environment-variables-for-infrastructure-configuration" 6 | 7 | link.xref_links.update({link_name: (link_text, link_url)}) 8 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /docs/development-environment/getting_started.rst: -------------------------------------------------------------------------------- 1 | Development environment 2 | ####################### 3 | 4 | Introduction 5 | ============ 6 | Mautic is a system based on the :xref:`Symfony framework` and :xref:`Doctrine`. 7 | 8 | Mautic uses the :xref:`Symfony framework` as a foundation for the app. 9 | 10 | It follows the :xref:`Symfony coding standards` and :xref:`Symfony best practices`. 11 | 12 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /docs/webhooks/events/lead_post_save_update.rst: -------------------------------------------------------------------------------- 1 | Contact updated event 2 | ##################### 3 | 4 | Triggered when Mautic updates a Contact. 5 | 6 | .. _contact_updated_event_type: 7 | 8 | Event type 9 | ********** 10 | 11 | ``mautic.lead_post_save_update`` 12 | 13 | .. _contact_updated_event_properties: 14 | 15 | Event properties 16 | **************** 17 | See :ref:`Lead post save ` event properties. -------------------------------------------------------------------------------- /.github/holopin.yml: -------------------------------------------------------------------------------- 1 | organization: mautic 2 | holobytes: 3 | - evolvingStickerId: cm1ti4x4c57560cjq2styaitm 4 | icon: avocado 5 | alias: hacktoberfest-2024 6 | 7 | - evolvingStickerId: cmg9s1kkz0046l204bc2k5k7e 8 | icon: avocado 9 | alias: hacktoberfest-2025 10 | 11 | # First time contributor badge 12 | stickers: 13 | - id: cmc38nn5r166207icbleca9jp 14 | name: First Contribution 15 | alias: first-contribution 16 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | white-space: normal !important; 11 | word-break: break-word !important; 12 | } 13 | 14 | .wy-table-responsive th p { 15 | margin-bottom: unset; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /docs/webhooks/events/email_on_open.rst: -------------------------------------------------------------------------------- 1 | Email open event 2 | ################ 3 | 4 | Triggered when a Contact presumably opens an Email. 5 | 6 | .. Note:: Email clients may load the tracking pixel through a proxy or scanned by bots and so may not be a reliable metric. 7 | 8 | .. _email_on_open_event_type: 9 | 10 | Event type 11 | ********** 12 | 13 | ``mautic.email_on_open`` 14 | 15 | .. _email_on_open_event_properties: 16 | 17 | Event properties 18 | **************** 19 | 20 | See :ref:`Event properties for the Email send event`. 21 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2013 Mautic 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /docs/plugins/dependencies.rst: -------------------------------------------------------------------------------- 1 | Plugin dependencies 2 | ################### 3 | 4 | You have a couple of options when it comes to dependencies. 5 | 6 | The first is to include dependencies with your distributable Plugin package. However, this isn't recommended as other Plugins may also include the same dependencies and conflict with your own. 7 | 8 | The recommended approach is to use Composer and make the Plugin available through the new Marketplace. 9 | 10 | :ref:`marketplace/listing:Preparing your Plugin for the Marketplace` by leveraging :xref:`Mautic's Composer plugin`. For detailed instructions, refer to the :doc:`/marketplace/listing`. 11 | -------------------------------------------------------------------------------- /docs/themes/system.rst: -------------------------------------------------------------------------------- 1 | Overriding core view templates 2 | ############################## 3 | 4 | You can override any view's template in Mautic by creating a ``themes/system`` directory, copying the core template file into a corresponding directory and customizing the HTML. 5 | 6 | For example, to customize the login page, copy ``app/bundles/UserBundle/Resources/views/Security/login.html.twig`` into ``themes/system/UserBundle/Resources/views/Security/login.html.twig`` then make the desired changes. 7 | 8 | .. Warning:: Customizations may break Mautic updates core templates in future versions. It's best practice to review and reconcile customizations with core templates after each upgrade. -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | "settings": { 11 | "python.defaultInterpreterPath": "/usr/local/bin/python" 12 | }, 13 | "extensions": [ 14 | "ms-python.python", 15 | "swyddfa.esbonio", 16 | "lextudio.restructuredtext", 17 | "eamodio.gitlens", 18 | "trond-snekvik.simple-rst" 19 | ] 20 | } 21 | }, 22 | "postCreateCommand": "chmod +x .devcontainer/setup-project.sh && .devcontainer/setup-project.sh" 23 | } 24 | -------------------------------------------------------------------------------- /docs/webhooks/events/index.rst: -------------------------------------------------------------------------------- 1 | Webhook events and payloads 2 | =========================== 3 | Webhook events are specific actions that occur in the system, such as when a Contact creates a new account. When an event occurs, Mautic sends a payload containing data about the event to the registered Webhook URL. 4 | 5 | Below is a list of documented events with their event types and the structure of their payloads: 6 | 7 | .. toctree:: 8 | :caption: Events 9 | :titlesonly: 10 | 11 | lead_post_save_new 12 | lead_post_save_update 13 | lead_points_change 14 | lead_post_delete 15 | lead_channel_subscription_changed 16 | lead_company_change 17 | company_post_save 18 | company_post_delete 19 | email_on_send 20 | email_on_open 21 | form_on_submit 22 | page_on_hit 23 | sms_on_send 24 | 25 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /docs/webhooks/events/lead_post_delete.rst: -------------------------------------------------------------------------------- 1 | Contact deleted event 2 | ##################### 3 | 4 | Triggered when Mautic deletes a Contact. 5 | 6 | .. _contact_deleted_event_type: 7 | 8 | Event type 9 | ********** 10 | 11 | ``mautic.lead_post_delete`` 12 | 13 | .. _contact_deleted_event_properties: 14 | 15 | Event properties 16 | **************** 17 | 18 | .. list-table:: 19 | :header-rows: 1 20 | 21 | * - Key 22 | - Type 23 | - Description 24 | * - ``id`` 25 | - int 26 | - ID of the deleted Contact 27 | * - ``company`` 28 | - object 29 | - :ref:`Contact object`. Note that ``id`` is null in this context. Use the ``id`` in the event instead. 30 | * - ``timestamp`` 31 | - string 32 | - Date/time the event occurred in ISO 8601 format. -------------------------------------------------------------------------------- /docs/webhooks/events/sms_on_send.rst: -------------------------------------------------------------------------------- 1 | Text send event 2 | ############### 3 | 4 | Triggered when Mautic sends a Text Message to a Contact. 5 | 6 | .. _sms_on_send_event_type: 7 | 8 | Event type 9 | ********** 10 | 11 | ``mautic.sms_on_send`` 12 | 13 | .. _sms_on_send_event_properties: 14 | 15 | Event properties 16 | **************** 17 | 18 | .. list-table:: 19 | :header-rows: 1 20 | 21 | * - Key 22 | - Type 23 | - Description 24 | * - ``smsId`` 25 | - int 26 | - ID of the SMS sent. 27 | * - ``contact`` 28 | - object 29 | - :ref:`Contact object`. 30 | * - ``content`` 31 | - string 32 | - Content of the SMS sent to the Contact. 33 | * - ``timestamp`` 34 | - string 35 | - Date/time the event occurred in ISO 8601 format. -------------------------------------------------------------------------------- /docs/webhooks/events/company_post_delete.rst: -------------------------------------------------------------------------------- 1 | Company deleted event 2 | ##################### 3 | 4 | Triggered when Mautic deletes a Company. 5 | 6 | .. _company_deleted_updated_event_type: 7 | 8 | Event type 9 | ********** 10 | 11 | ``mautic.company_post_delete`` 12 | 13 | .. _company_deleted_updated_event_properties: 14 | 15 | Event properties 16 | **************** 17 | 18 | .. list-table:: 19 | :header-rows: 1 20 | 21 | * - Key 22 | - Type 23 | - Description 24 | * - ``id`` 25 | - int 26 | - ID of the deleted Company 27 | * - ``company`` 28 | - object 29 | - :ref:`Company object`. Note that ``id`` is null in this context. Use the ``id`` in the event instead. 30 | * - ``timestamp`` 31 | - string 32 | - Date/time the event occurred in ISO 8601 format. -------------------------------------------------------------------------------- /docs/plugins/translations.rst: -------------------------------------------------------------------------------- 1 | Translating Plugins 2 | ################### 3 | 4 | Plugins include their own translations in their ``Translations`` directories organized by locale. Currently, only :xref:`Core translations are supported through Transifex`. See :ref:`components/translator:Translator` for information on writing translations and using the Translator service in your Plugin. 5 | 6 | .. note:: All Plugins must include a US English, en_US, translation. 7 | 8 | Below is an example Plugin's translations:: 9 | 10 | Translations/ 11 | en_US/ 12 | flashes.ini 13 | messages.ini 14 | validators.ini 15 | es_MX/ 16 | flashes.ini 17 | messages.ini 18 | validators.ini 19 | fr_FR/ 20 | flashes.ini 21 | messages.ini 22 | validators.ini 23 | -------------------------------------------------------------------------------- /.ddev/commands/web/build-docs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## Description: Build User documentation using the Sphinx HTML builder. 4 | ## Usage: build-docs 5 | ## Example: ddev build-docs 6 | 7 | # Ensure we fail on error 8 | set -e 9 | 10 | # Initialize terminal colors/styles 11 | bold=$(tput bold) 12 | green=$(tput setaf 2) 13 | reset=$(tput sgr0) 14 | underline=$(tput smul) 15 | 16 | echo "${bold}${green}🔧 Building user documentation...${reset}" 17 | 18 | # Ensure we're in the right directory 19 | cd /var/www/html/docs 20 | echo 21 | 22 | # Run the build 23 | make html 24 | 25 | # Success message 26 | echo 27 | echo "${green}✅ Documentation build completed successfully!${reset}" 28 | echo "${green}📁 The HTML pages are in: ${bold}/var/www/html/docs/build/html${reset}" 29 | 30 | # Friendly access hint 31 | echo "${green}🌐 View the documentation at: ${bold}${underline}https://${DDEV_HOSTNAME}${reset}" 32 | echo -------------------------------------------------------------------------------- /docs/plugins/getting_started.rst: -------------------------------------------------------------------------------- 1 | Getting started with Plugins 2 | ############################ 3 | 4 | Plugins are Symfony bundles that can extend the capability of Mautic. They can be very simple or complex and have access to leverage nearly all that Symfony offers. Just as a reminder, this covers the basics. If you require more advanced features of Symfony, the :xref:`Symfony documentation` is a valuable resource. 5 | 6 | .. note:: Plugins install into the ``plugins/`` directory. Namespace all classes with ``MauticPlugin\[Plugin Folder Name]\``. 7 | 8 | .. warning:: Avoid using Symfony's ability to override core classes with hacked versions in a Plugin. Doing so creates compatibility challenges with future upgrades and/or may conflict with another Plugin that's doing the same thing. Rather, build into core the support you need to enable what you are wanting to accomplish with your Plugin then contribute it to the :xref:`project as a PR`. -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/webhooks/events/lead_company_change.rst: -------------------------------------------------------------------------------- 1 | Contact Company subscription change event 2 | ######################################### 3 | 4 | Triggered when Mautic adds or removes a Contact to/from a Company. 5 | 6 | .. _company_subscription_change_event_type: 7 | 8 | Event type 9 | ********** 10 | 11 | ``mautic.lead_company_change`` 12 | 13 | .. _company_subscription_change_event_properties: 14 | 15 | Event properties 16 | **************** 17 | 18 | .. list-table:: 19 | :header-rows: 1 20 | 21 | * - Key 22 | - Type 23 | - Description 24 | * - ``added`` 25 | - boolean 26 | - ``TRUE`` if Contact added to the Company. ``FALSE`` if removed. 27 | * - ``contact`` 28 | - object 29 | - :ref:`Contact object`. 30 | * - ``company`` 31 | - object 32 | - :ref:`Company object`. 33 | * - ``timestamp`` 34 | - string 35 | - Date/time the event occurred in ISO 8601 format. -------------------------------------------------------------------------------- /docs/webhooks/events/lead_points_change.rst: -------------------------------------------------------------------------------- 1 | Contact Points changed event 2 | ############################ 3 | 4 | Triggered when Mautic modifies a Contact's points. 5 | 6 | .. _contact_points_changed_event_type: 7 | 8 | Event type 9 | ********** 10 | 11 | ``mautic.lead_points_change`` 12 | 13 | .. _contact_points_changed_event_properties: 14 | 15 | Event properties 16 | **************** 17 | 18 | .. list-table:: 19 | :header-rows: 1 20 | 21 | * - Key 22 | - Type 23 | - Description 24 | * - ``contact`` 25 | - object 26 | - :ref:`Contact object`. 27 | * - ``points`` 28 | - object 29 | - Contains the original Points and the new Points for the Contact. 30 | * - ``points.old_points`` 31 | - int 32 | - The Contact's original Points. 33 | * - ``points.new_points`` 34 | - int 35 | - The Contact's new Points. 36 | * - ``timestamp`` 37 | - string 38 | - Date/time the event occurred in ISO 8601 format. -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /docs/marketplace/getting_started.rst: -------------------------------------------------------------------------------- 1 | Marketplace 2 | ########### 3 | 4 | Mautic 4 comes with a Marketplace directly in the Mautic administration user interface and command line interface as well. 5 | 6 | - **Mautic 4.0.0** introduced the read-only Marketplace, which only lists available Plugins. Installing Plugins isn't possible in this version. 7 | - **Mautic 4.2.0** introduced the ability to install and remove Plugins through the Marketplace. 8 | 9 | Todo (SCREENSHOTS) add screenshots here. 10 | 11 | Getting your Plugin listed in the Marketplace 12 | ********************************************* 13 | 14 | There are two steps involved in listing your Plugin in the Marketplace: 15 | 16 | 1. Create and test your Plugin locally - see Todo (LINK) for more details. 17 | 2. :doc:`Prepare your Plugin for listing in the Marketplace <./listing>` 18 | 19 | Marketplace under the hood 20 | ************************** 21 | 22 | The Marketplace uses Packagist and Composer v2 under the hood. Packagist's API lists the Mautic Plugins and finds the Plugin details. Composer v2 installs and updates the Plugins. Composer takes care of the dependencies of your Plugin, and also compatibility with different Mautic and PHP versions. -------------------------------------------------------------------------------- /docs/ext/xref.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from docutils import nodes 4 | from sphinx.util import caption_ref_re 5 | 6 | def xref( typ, rawtext, text, lineno, inliner, options={}, content=[] ): 7 | 8 | title = target = text 9 | titleistarget = True 10 | # look if explicit title and target are given with `foo ` syntax 11 | brace = text.find('<') 12 | if brace != -1: 13 | titleistarget = False 14 | m = caption_ref_re.match(text) 15 | if m: 16 | target = m.group(2) 17 | title = m.group(1) 18 | else: 19 | # fallback: everything after '<' is the target 20 | target = text[brace+1:] 21 | title = text[:brace] 22 | 23 | link = xref.links[target] 24 | 25 | if brace != -1: 26 | pnode = nodes.reference(target, title, refuri=link[1]) 27 | else: 28 | pnode = nodes.reference(target, link[0], refuri=link[1]) 29 | 30 | return [pnode], [] 31 | 32 | def get_refs(app): 33 | 34 | xref.links = app.config.xref_links 35 | 36 | def setup(app): 37 | 38 | app.add_config_value('xref_links', {}, True) 39 | app.add_role('xref', xref) 40 | app.connect("builder-inited", get_refs) -------------------------------------------------------------------------------- /docs/marketplace/best_practices.rst: -------------------------------------------------------------------------------- 1 | Best practices 2 | ############## 3 | 4 | When creating Plugins for Mautic, there's a few best practices which are highly recommend: 5 | 6 | 1. The Marketplace links to the GitHub repository. Make sure you have all the information your users need in the README.md file. 7 | 2. The GitHub repository should have issues enabled, so that your users are able to report issues and search for solutions. There is direct link to GitHub issues in the Marketplace. 8 | 3. Use GitHub releases. Tag every new version. 9 | 4. Write down changelog to all the GitHub releases. Again, the Marketplace links to all of them. Keep your users informed about what has changed in each version. 10 | 5. Write automatic unit and functional tests and run them automatically with a CI like :xref:`GitHub Actions`. The effort you make to improve stability helps to catch bugs before they land with the users. 11 | 6. Take an advantage of releasing alpha and beta versions when working on big changes. Example beta version: ``1.2.0-beta``. 12 | 7. Use :xref:`Semantic versioning` starting with 1.0. Don't tie your Plugin version with Mautic's version. The require section in ``composer.json`` specifies which Mautic versions your Plugin supports. 13 | -------------------------------------------------------------------------------- /docs/ext/code_samples_ext.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import requests 4 | import os 5 | 6 | def get_code_samples(app): 7 | 8 | print("Getting code samples from GitHub... This might take a while!") 9 | 10 | code_samples = app.config.code_samples 11 | code_samples_downloaded_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + "/code_samples_downloaded" 12 | 13 | for key in code_samples: 14 | # E.g. code_samples_downloaded/Entity_World.php 15 | file_path = code_samples_downloaded_dir + "/" + key 16 | 17 | # If the file exists already, skip downloading it, so that we don't overload GitHub with requests 18 | if (os.path.exists(file_path)): 19 | print(key + " exists already. Skipping download.") 20 | continue 21 | 22 | print("Downloading " + code_samples[key] + "...") 23 | 24 | # Get the code from GitHub and copy into the file (key), e.g. Entity_World 25 | r = requests.get(code_samples[key], allow_redirects=True) 26 | open(code_samples_downloaded_dir + "/" + key, 'wb').write(r.content) 27 | 28 | def setup(app): 29 | 30 | app.add_config_value('code_samples', {}, True) 31 | app.connect("builder-inited", get_code_samples) 32 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | 4 | 5 | 6 | 13 | 14 | ## Linked issue 15 | 16 | 17 | 18 | 19 | 30 | 31 | ## Screenshots or screen recordings 32 | 33 | -------------------------------------------------------------------------------- /docs/webhooks/events/lead_channel_subscription_changed.rst: -------------------------------------------------------------------------------- 1 | Contact Channel subscription change event 2 | ######################################### 3 | 4 | Triggered when Mautic changes a Contact's Channel subscription status. 5 | 6 | .. _channel_subscription_changed_event_type: 7 | 8 | Event type 9 | ********** 10 | 11 | ``mautic.lead_channel_subscription_changed`` 12 | 13 | .. _channel_subscription_changed_event_properties: 14 | 15 | Event properties 16 | **************** 17 | 18 | .. list-table:: 19 | :header-rows: 1 20 | 21 | * - Key 22 | - Type 23 | - Description 24 | * - ``contact`` 25 | - object 26 | - :ref:`Contact object`. 27 | * - ``channel`` 28 | - string 29 | - The Channel unsubscribed from. Examples are ``email`` and ``sms``. 30 | * - ``old_status`` 31 | - string 32 | - The Contact's original state for the given Channel. Options are ``contactable``, ``bounced``, ``manual``, and ``unsubscribed``. 33 | * - ``new_status`` 34 | - string 35 | - The Contact's new state for the given Channel. Options are ``contactable``, ``bounced``, ``manual``, and ``unsubscribed`` 36 | * - ``timestamp`` 37 | - string 38 | - Date/time the event occurred in ISO 8601 format. -------------------------------------------------------------------------------- /docs/development-environment/how_to_install_with_ddev.rst: -------------------------------------------------------------------------------- 1 | How to install Mautic with DDEV 2 | ############################### 3 | 4 | .. note:: 5 | If you get stuck, join the lively Mautic Community on :xref:`Mautic Slack` or the :xref:`Developer Forum` for support and answers. **Please first post in the forum**, then share the link in Slack, so others can learn from your question. 6 | 7 | Pre-requisites with DDEV 8 | ======================== 9 | 1. You should have DDEV and Docker or Colima installed on your machine. If not, please follow the instructions here: :xref:`ddev install` 10 | 11 | Installing Mautic is a two-step process: 12 | ======================================== 13 | 1. Clone this repository 14 | 15 | .. code-block:: bash 16 | 17 | git clone https://github.com/mautic/mautic.git 18 | 19 | .. Note:: Clone the repository in the directory where you want to install Mautic. 20 | 21 | 1. Install Mautic running DDEV. 22 | 23 | .. vale off 24 | 25 | .. code-block:: bash 26 | 27 | cd mautic 28 | ddev start 29 | 30 | .. vale on 31 | 32 | .. list-table:: Users and passwords 33 | :header-rows: 1 34 | 35 | * - Username 36 | - Password 37 | * - ``admin`` 38 | - ``Maut1cR0cks!`` 39 | * - ``sales`` 40 | - ``Maut1cR0cks!`` 41 | 42 | .. Note:: Versions of Mautic prior to 5.1 use the password ``mautic`` 43 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/02_feature_request.yml: -------------------------------------------------------------------------------- 1 | name: 🌱 Feature Request 2 | description: Request a new feature or enhancement. 3 | title: "[FEAT]:" 4 | labels: ["enhancement", "needs-triage"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thanks for suggesting a new feature for the Mautic Developer Documentation! Please fill out the details below to help us understand and prioritize it. 10 | 11 | - type: textarea 12 | id: description 13 | attributes: 14 | label: Description 15 | description: Please provide a clear and concise description of the feature you would like to see. 16 | placeholder: "e.g., I'd like to be able to edit the documentation directly on GitHub. This would be useful because..." 17 | validations: 18 | required: true 19 | 20 | - type: dropdown 21 | id: willingness-to-pr 22 | attributes: 23 | label: Are you willing to help us add this feature this by making a pull request? 24 | description: Contributing a pull request is the fastest way to get this fixed. 25 | options: 26 | - "— Select an option —" 27 | - "No" 28 | - "Yes" 29 | - "Yes, but I need help" 30 | validations: 31 | required: true 32 | 33 | - type: textarea 34 | id: suggestions 35 | attributes: 36 | label: Suggestions (Optional) 37 | description: How do you suggest this feature should be implemented? 38 | placeholder: "e.g., A user could click the edit button on the documentation page to make changes." -------------------------------------------------------------------------------- /docs/development-environment/setup.rst: -------------------------------------------------------------------------------- 1 | How to install Mautic manually 2 | ############################## 3 | 4 | Pre-requisites to setup 5 | ======================= 6 | 7 | Mautic assumed that the system already has ``composer`` and ``git`` installed and configured. 8 | 9 | .. note:: 10 | If you get stuck, join the lively Mautic Community on :xref:`Mautic Slack` or the :xref:`Developer Forum` for support and answers. **Please first post in the forum**, then share the link in Slack, so others can learn from your question. 11 | 12 | Steps 13 | ===== 14 | 1. To setup the developer environment, fork and clone the source from GitHub as outlined in :doc:`/development-environment/how_to_install_with_ddev`. 15 | 2. Run ``composer install`` on the source. 16 | 3. Open your browser and complete the installation through the Mautic installer. 17 | 18 | You can also run the install process from command line: 19 | 20 | * Add a ``local.php`` file in ``app/config`` 21 | * Edit the ``local.php`` file using the following template (Mautic adapts to new local settings): 22 | 23 | .. code-block:: php 24 | 25 | 'pdo_mysql', 28 | 'db_host' => 'localhost', 29 | 'db_table_prefix' => null, 30 | 'db_port' => '3306', 31 | 'db_name' => 'mautic', 32 | 'db_user' => 'root', 33 | 'db_password' => 'root_password', 34 | 'db_backup_tables' => true, 35 | 'db_backup_prefix' => 'bak_', 36 | ); 37 | 38 | * Run the following command and add your own options: 39 | 40 | .. code-block:: bash 41 | 42 | php bin/console mautic:install https://mautic.example.com -------------------------------------------------------------------------------- /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/marketplace/allowlist_what_and_why.rst: -------------------------------------------------------------------------------- 1 | Allow list: what is it and why is it needed? 2 | ############################################ 3 | 4 | Plugin development can be challenging. Mautic tries to make Plugin development as easy and smooth as possible, but there's always a risk that a Plugin breaks Mautic entirely. 5 | There are plans to build several safeguards into the Marketplace which should prevent Mautic from crashing when installing a Plugin, however this is currently a work in progress. That's why there is a so-called ``allowlist``, meaning that Mautic's core team has to approve your Plugin for it to show up in the Marketplace. This should ensure a higher level of stability for all Users of Mautic. 6 | 7 | How does it work? 8 | ***************** 9 | 10 | The process roughly looks like this: 11 | 12 | 1. Plugin author fills out the :xref:`Marketplace allowlist application form`. 13 | 2. Mautic's core team reviews the submission. 14 | 3. Once accepted, the core team adds the Plugin to the list at https://github.com/mautic/marketplace-allowlist. 15 | 16 | Applying for the allowlist 17 | ************************** 18 | 19 | As mentioned, you can use the :xref:`Marketplace allowlist application form` which gets reviewed by the core team. Once reviewed, the requester receives an update by Email. In the meantime, you can keep an eye on the allowlist at https://github.com/mautic/marketplace-allowlist to track the latest updates. 20 | 21 | Moving forward: roadmap 22 | *********************** 23 | 24 | - November 2021: release Mautic 4.1 with allowlist enabled for the Marketplace 25 | - TBD: switch from allowlist to blocklist - all Plugins allowed except the ones that are incompatible or have security issues 26 | 27 | -------------------------------------------------------------------------------- /.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 -------------------------------------------------------------------------------- /docs/themes/forms.rst: -------------------------------------------------------------------------------- 1 | Customizing Forms 2 | ################# 3 | 4 | To provide custom Form field templates or to manipulate the Form body, create the following directory structure:: 5 | 6 | Field/ <– for customizing form field types 7 | html/ 8 | MauticFormBundle/ 9 | Builder/ 10 | _style.html.twig <– for customizing CSS for Form 11 | form.html.twig <– for customizing the form structure itself 12 | 13 | Copy from ``app/bundles/FormBundle/Resources/views/form.html.twig`` into the Theme’s Builder directory and/or one or more of the fields templates in ``app/bundles/FormBundle/Views/Field/*.html.php`` into the Theme’s `field` directory. Then customize to the desired layout. 14 | 15 | You can add a custom style sheet to the Form by adding a ``_style.html.twig`` with your custom CSS to ``html/MauticFormBundle/Builder``. The best way is to copy the content of the default Form styles and modify them to your needs. 16 | 17 | Customize field types 18 | ********************* 19 | 20 | Create a new template in the `Field` directory to change HTML generated by field types. 21 | The best way is to copy the original template from `app/bundles/FormBundle/Resources/views/Field/*.html.twig` and modify it. 22 | 23 | 24 | How to modify templates including base `text.html.twig` template 25 | ================================================================ 26 | 27 | Several templates are including the base `text.html.twig` template so you may need modify it. In that case you also need to modify templates including it. Change the `include` statement. 28 | 29 | For example `email.html.twig` including: 30 | 31 | {{ include('@MauticForm/Field/text.html.twig', { ... 32 | 33 | you need change `@MauticForm` to you theme, for example: 34 | 35 | {{ include('@themes/MyTheme/Field/text.html.twig', { ... -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/01_documentation_fix.yml: -------------------------------------------------------------------------------- 1 | name: 📝 Documentation Fix 2 | description: Point out an error or suggest a change in the documentation. 3 | title: "[DOCS]:" 4 | labels: ["documentation", "needs-triage"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thanks for helping improve the Mautic Developer Documentation! Please fill out the details below so we can fix it. 10 | 11 | - type: textarea 12 | id: description 13 | attributes: 14 | label: Description 15 | description: Please describe what is inaccurate, missing, or confusing in the documentation. 16 | placeholder: "e.g., The example code on page X is out of date and no longer works." 17 | validations: 18 | required: true 19 | 20 | - type: textarea 21 | id: doc-link 22 | attributes: 23 | label: Relevant Documentation URL 24 | description: Please provide a link to the exact documentation page that needs to be updated. 25 | placeholder: "e.g., https://devdocs.mautic.org/page-to-fix" 26 | 27 | - type: dropdown 28 | id: willingness-to-pr 29 | attributes: 30 | label: Are you willing to help us fix this by making a pull request? 31 | description: Contributing a pull request is the fastest way to get this fixed. 32 | options: 33 | - "— Select an option —" 34 | - "No" 35 | - "Yes" 36 | - "Yes, but I need help" 37 | validations: 38 | required: true 39 | 40 | - type: textarea 41 | id: suggestions 42 | attributes: 43 | label: Suggestions (Optional) 44 | description: How do you suggest the documentation could be fixed or improved? 45 | placeholder: "e.g., I think changing the example code to this would solve the issue..." 46 | 47 | - type: textarea 48 | id: screenshots 49 | attributes: 50 | label: Screenshots or Screen Recordings (Optional) 51 | description: To help us understand the issue visually, please attach any relevant images or recordings. 52 | placeholder: "Drag and drop the files here." -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /docs/design/availability.rst: -------------------------------------------------------------------------------- 1 | Depicting availability of interface elements 2 | ############################################ 3 | 4 | The state of interface elements is a crucial aspect of user interface design, providing visual feedback and preventing interaction when certain actions aren't allowed. 5 | 6 | Working with tabs 7 | ***************** 8 | 9 | Mautic uses the following CSS code to style tabs which aren't available for interaction: 10 | 11 | .. code-block:: css 12 | 13 | .nav-tabs.nav-tabs-contained > li.disabled { 14 | cursor: not-allowed; 15 | color: var(--text-disabled); 16 | } 17 | 18 | .nav-tabs.nav-tabs-contained > li.disabled > a { 19 | background-color: var(--button-disabled); 20 | pointer-events: none; 21 | } 22 | 23 | This CSS accomplishes the following: 24 | 25 | * Sets the cursor to ``not-allowed`` for tabs which can't be interacted with, indicating that interaction is prohibited. 26 | * Changes the text color to a predefined inactive state color. 27 | * Modifies the background color of the tab to visually represent its inactive state. 28 | * Prevent click events on the tab using ``pointer-events: none``. 29 | 30 | To dynamically deactivate tabs, use JavaScript to add or remove the ``disabled`` class. Here's an example function: 31 | 32 | .. code-block:: javascript 33 | 34 | Mautic.togglePermissionVisibility = function () { 35 | setTimeout(function () { 36 | if (mQuery('#role_isAdmin_0').prop('checked')) { 37 | mQuery('#permissions-tab').removeClass('disabled'); 38 | } else { 39 | mQuery('#permissions-tab').addClass('disabled'); 40 | } 41 | }, 10); 42 | }; 43 | 44 | This function: 45 | 46 | * Checks the state of a checkbox - ``#role_isAdmin_0``. 47 | * Adds or removes the ``disabled`` class from the permissions tab based on the checkbox state. 48 | 49 | To implement inactive states for tabs: 50 | 51 | 1. Assign unique IDs to your tab elements. 52 | 2. Use JavaScript to toggle the ``disabled`` class on the appropriate ``
  • `` elements. 53 | 54 | .. note:: 55 | Always use inactive states instead of hiding elements. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/03_bug_report.yml: -------------------------------------------------------------------------------- 1 | name: 🐛 Technical Bug Report 2 | description: Did you find a bug with the documentation system or automations? Create a report to help us improve. 3 | title: "[BUG]:" 4 | labels: ["bug", "needs-triage"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thanks for taking the time to report a technical bug within the Mautic Developer Documentation! Please fill out the details below to help us understand and resolve it. 10 | 11 | - type: textarea 12 | id: description 13 | attributes: 14 | label: Description 15 | description: Provide a clear and concise description of the bug. Include steps to reproduce the behavior, the expected result, and the actual result. 16 | placeholder: "e.g., Running make html command didn't build the docs. Steps to reproduce: 1. Go to the /docs directory. 2. Run make html command. 3. Nothing happens." 17 | validations: 18 | required: true 19 | 20 | - type: dropdown 21 | id: willingness-to-pr 22 | attributes: 23 | label: Are you willing to help us fix this by making a pull request? 24 | description: Contributing a pull request is the fastest way to get this fixed. 25 | options: 26 | - "— Select an option —" 27 | - "No" 28 | - "Yes" 29 | - "Yes, but I need help" 30 | validations: 31 | required: true 32 | 33 | - type: textarea 34 | id: suggestions 35 | attributes: 36 | label: Suggestions (Optional) 37 | description: How do you suggest this bug could be fixed? 38 | placeholder: "e.g., I think changing X to Y would solve the issue." 39 | 40 | - type: textarea 41 | id: environment 42 | attributes: 43 | label: Environment 44 | description: | 45 | Please provide details about your environment where the bug occurred. 46 | - OS/Platform 47 | - Browser & Version 48 | placeholder: "e.g., Windows 11, Chrome v125" 49 | 50 | - type: textarea 51 | id: screenshots 52 | attributes: 53 | label: Screenshots or Screen Recordings (Optional) 54 | description: To help us understand the issue visually, please attach any relevant images or recordings. 55 | placeholder: "Drag and drop the files here." -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | "login": "rfay", 65 | "name": "Randy Fay", 66 | "avatar_url": "https://avatars.githubusercontent.com/u/112444?v=4", 67 | "profile": "http://ddev.com", 68 | "contributions": [ 69 | "doc" 70 | ] 71 | } 72 | ], 73 | "contributorsPerLine": 7, 74 | "projectName": "developer-documentation-new", 75 | "projectOwner": "mautic", 76 | "repoType": "github", 77 | "repoHost": "https://github.com", 78 | "skipCi": true, 79 | "commitConvention": "angular", 80 | "commitType": "docs" 81 | } 82 | -------------------------------------------------------------------------------- /docs/design/protip.rst: -------------------------------------------------------------------------------- 1 | ProTip template to enhancing user experience 2 | ############################################ 3 | 4 | The ProTip template is a powerful feature that displays helpful tips to Users, guiding them on how to use the platform like a pro. This template uses Twig. Here's how this template works and how to effectively use it in Mautic. 5 | 6 | Twig template for ProTips 7 | ************************* 8 | 9 | The ProTip template conditionally renders a tip when provided. Here's a breakdown of its key components: 10 | 11 | .. code-block:: twig 12 | 13 | {% if tip is defined and tip is not empty %} 14 |
    15 |
    16 | 17 | {{ 'mautic.core.protip'|trans }} 18 | {{ tip|trans|raw }} 19 |
    20 |
    21 | {% endif %} 22 | 23 | This template checks for the definition of a ``tip`` variable. If defined, it renders a div containing the tip. Mautic displays the tip with a bulb icon, a "ProTip" label - a translatable string - and the tip content itself. 24 | 25 | The ``tip`` parameter's passed through Twig's ``trans`` filter, allowing for internationalization. The ``raw`` filter's also applied, enabling the use of HTML tags within the tip content. This is particularly useful for including elements like ```` tags to highlight keyboard shortcuts. 26 | 27 | To include a ProTip in Mautic, use the following syntax: 28 | 29 | .. code-block:: twig 30 | 31 | {{ include('@MauticCore/Helper/protip.html.twig', {tip: 'mautic.core.protip.contacts.view'}) }} 32 | 33 | In this example, it includes the ProTip template and passes a translation key - ``mautic.core.protip.contacts.view`` - as the tip content. This specific tip informs Users about using the ``V`` key to switch between card and table views. 34 | 35 | The use of a translation key instead of a hard-coded string allows for easy localization of tips. It also promotes consistency across Mautic, as it's possible to reuse the same tip in multiple places by referencing its key. 36 | 37 | Use the ProTip template to enhance the user experience of Mautic. These tips provide contextual help, highlighting shortcuts and features that Users might otherwise overlook. It's important to keep tips concise, relevant, and actionable to maximize their effectiveness in guiding Users to become 'Power Users'. -------------------------------------------------------------------------------- /docs/plugins/structure.rst: -------------------------------------------------------------------------------- 1 | File and directory structure 2 | ############################ 3 | 4 | The directory structure of a Plugin varies based on the features implemented. 5 | 6 | Plugins require the following structure at a minimum:: 7 | 8 | HelloWorldBundle/ 9 | Config/ 10 | config.php 11 | HelloWorldBundle.php 12 | 13 | ``HelloWorldBundle/Config/config.php`` registers the Plugin with Mautic along with defining routes, menu items, services, and parameters. 14 | 15 | The ``HelloWorldBundle.php`` file registers the bundle with Symfony's kernel. Extend the class with ``Mautic\IntegrationsBundle\Bundle\AbstractPluginBundle``. 16 | 17 | .. code-block:: php 18 | 19 | 35 | 36 | The API name is the value for the ``data-mautic-form`` attribute which in this case is ``thisismytestform``. 37 | 38 | You'll define the Form's hooks as JavaScript methods in the correlated ``MauticFormCallback`` index. For example, to add custom validation, you would script something like the following: 39 | 40 | .. code-block:: javascript 41 | 42 | MauticFormCallback['formname1'] = { 43 | // other hooks 44 | 45 | onValidate: function () { 46 | var email = document.getElementById('mauticform_input_formname1_email').value; 47 | if (email.includes('@gmail.com')) { 48 | alert('Please use a work email address.'); 49 | 50 | // return FALSE to stop the Form submission 51 | return FALSE; 52 | } 53 | 54 | // return NULL|void to continue with default validation or return TRUE to skip it 55 | }, 56 | 57 | // more hooks 58 | }; 59 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /docs/development-environment/environments.rst: -------------------------------------------------------------------------------- 1 | Environments 2 | ############ 3 | 4 | :xref:`Symfony environment conventions` 5 | 6 | In all environments, Mautic loads the following files if they exist, 7 | the latter taking precedence over the former: 8 | 9 | * ``.env`` contains default values for the environment variables needed by the app 10 | * ``.env.local`` uncommitted file with local overrides 11 | * ``.env.$APP_ENV`` committed environment-specific defaults 12 | * ``.env.$APP_ENV.local`` uncommitted environment-specific overrides 13 | 14 | Real environment variables win over ``.env`` files. 15 | 16 | .. warning:: Don't define secrets in this file, or any other committed files. Set secrets via environment variables, or through other secret management tools. 17 | 18 | Run ``composer dump-env prod`` to compile .env files for production use (``requires symfony/flex >=1.2``). Read more about 19 | :xref:`best practices`. 20 | 21 | By default the structure come with 3 environments 22 | ``.env`` 23 | ``.env.test`` 24 | ``.env.test.local`` 25 | 26 | Mautic loads default values filled in the ``app/config/parameters.php`` file at installation. 27 | These values can be overridden by the ``.env`` structure. 28 | 29 | Development environment 30 | ======================= 31 | ``.env`` come with two values: 32 | 33 | .. code-block:: bash 34 | 35 | APP_ENV=prod 36 | APP_DEBUG=0 37 | 38 | It's recommended to create the ``.env.local`` file. This file overrides the values in ``.env`` file. 39 | Example: 40 | 41 | .. code-block:: bash 42 | 43 | APP_ENV=dev 44 | APP_DEBUG=1 45 | DB_HOST=... 46 | DB_PORT=3306 47 | DB_NAME=... 48 | DB_USER=... 49 | DB_PASSWD=... 50 | MAUTIC_DB_PREFIX=... 51 | MAUTIC_TABLE_PREFIX=... 52 | 53 | Test environment 54 | ================ 55 | 56 | In the test environment there are two files. 57 | 58 | The first one is the default file ``.env.test`` that includes credentials used to create a test instance of Mautic. 59 | 60 | .. code-block:: bash 61 | 62 | DB_HOST=127.0.0.1 63 | DB_PORT=3306 64 | DB_NAME=mautictest 65 | DB_USER=root 66 | DB_PASSWD= 67 | MAUTIC_DB_PREFIX=test_ 68 | MAUTIC_TABLE_PREFIX=test_ 69 | MAUTIC_ENV=test 70 | MAUTIC_ADMIN_USERNAME=admin 71 | MAUTIC_ADMIN_PASSWORD=Maut1cR0cks! 72 | 73 | The second one, ``.env.test.local`` includes values for the DDEV local development environment, overriding the ``.env.test`` values. 74 | It's recommended making any changes to this file with its values. 75 | 76 | .. code-block:: bash 77 | 78 | DB_HOST=db 79 | DB_USER=db 80 | DB_PASSWD=db 81 | DB_NAME=test 82 | ... 83 | MAUTIC_DB_PREFIX=... 84 | -------------------------------------------------------------------------------- /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/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/design/retrieving_system_information.rst: -------------------------------------------------------------------------------- 1 | Retrieving Mautic settings in Twig 2 | ################################## 3 | 4 | Mautic allows you to access configuration settings directly in Twig templates using the ``configGetParameter`` function. This feature is particularly useful for creating display conditions or showing existing data in your templates. 5 | 6 | Basic usage 7 | *********** 8 | 9 | To retrieve a setting, use the ``configGetParameter`` function with the parameter name as its argument: 10 | 11 | .. code-block:: twig 12 | 13 | {{ configGetParameter('parameter_name') }} 14 | 15 | Display conditions 16 | ================== 17 | 18 | Use ``configGetParameter`` in conditional statements to control the display of content based on configuration settings: 19 | 20 | .. code-block:: twig 21 | 22 | {% if configGetParameter('ip_lookup_auth') %} 23 | 24 | {% endif %} 25 | 26 | Displaying configuration values 27 | =============================== 28 | 29 | To directly display a configuration value in your template, use the following syntax: 30 | 31 | .. code-block:: twig 32 | 33 | {{ configGetParameter('parameter_name') }} 34 | 35 | It's also possible to define the default value as a second parameter: 36 | 37 | .. code-block:: twig 38 | 39 | {{ configGetParameter('parameter_name', 'default value') }} 40 | 41 | For example, to display the API OAuth2 access token lifetime: 42 | 43 | .. code-block:: twig 44 | 45 | API OAuth2 Access Token Lifetime: {{ configGetParameter('api_oauth2_access_token_lifetime') }} 46 | 47 | Finding available parameters 48 | ============================ 49 | 50 | The ``/config/local.php`` file contains available configuration parameters, once you save the global configuration form for the first time. This file contains the complete list of settings that are accessible using ``configGetParameter``. 51 | 52 | Identifying parameter names 53 | =========================== 54 | 55 | To find the correct parameter name for a specific setting: 56 | 57 | 1. Inspect the HTML of the setting field in the Mautic interface. 58 | 2. Look for the ``name`` attribute of the input field. 59 | 3. The parameter name is the last part of the ``name`` attribute value. 60 | 61 | For example, if you see: 62 | 63 | .. code-block:: html 64 | 65 | 66 | 67 | The corresponding parameter name would be ``api_oauth2_access_token_lifetime``. 68 | 69 | Additional information 70 | ********************** 71 | 72 | - Be cautious when displaying sensitive configuration data in templates. 73 | - Always consider providing default values when using configuration parameters to handle cases where the setting aren't defined. 74 | 75 | Using the ``configGetParameter`` function in Twig, you can create new interactive experiences in Mautic. -------------------------------------------------------------------------------- /docs/form_hooks/general_hooks.rst: -------------------------------------------------------------------------------- 1 | General hooks 2 | ############# 3 | 4 | .. js:method:: onSubmitButtonEnable() 5 | 6 | Called prior to default enabling of the submit button. 7 | 8 | :returns: bool|null Return TRUE to skip the default behavior that re-enables the submit button. 9 | 10 | .. code-block:: javascript 11 | 12 | MauticFormCallback['formname'] = { 13 | onSubmitButtonEnable: function () { 14 | // do some custom stuff 15 | }, 16 | }; 17 | 18 | .. js:method:: onSubmitButtonDisable() 19 | 20 | Called prior to default disabling of the submit button after the Form has been submitted. 21 | 22 | :returns: bool|null Return TRUE to skip the default behavior that disables the submit button. 23 | 24 | .. code-block:: javascript 25 | 26 | MauticFormCallback['formname'] = { 27 | onSubmitButtonDisable: function () { 28 | // do some custom stuff 29 | }, 30 | }; 31 | 32 | .. js:method:: onShowNextPage() 33 | 34 | Called prior to displaying the next page in the Form which is useful to adjust the DOM prior to making the page visible. 35 | 36 | :param number pageNumber: The page number to display. 37 | :returns: void 38 | 39 | .. code-block:: javascript 40 | 41 | MauticFormCallback['formname'] = { 42 | onShowNextPage: function (pageNumber) { 43 | // do some custom stuff 44 | }, 45 | }; 46 | 47 | .. js:method:: onShowPreviousPage() 48 | 49 | Called prior to displaying the previous page in the Form which is useful to adjust the DOM prior to making the page visible. 50 | 51 | :param number pageNumber: The page number to display. 52 | :returns: void 53 | 54 | .. code-block:: javascript 55 | 56 | MauticFormCallback['formname'] = { 57 | onShowPreviousPage: function (pageNumber) { 58 | // do some custom stuff 59 | }, 60 | }; 61 | 62 | .. js:method:: onMessageSet() 63 | 64 | Called prior to injecting text into the corresponding elements to the message type which happens before Form validation to clear existing text from previous submissions and after the Form is validated with either the validation error or success message. 65 | 66 | :param object messageObject: 67 | * ``messageObject.message`` The text to inject. 68 | * ``messageObject.type`` This will either be ``error`` or ``message``. 69 | 70 | :returns: bool|NULL|void Return TRUE to prevent the default behavior to inject the message into the corresponding element (for example if the hook injected the message elsewhere). Return NULL|void to continue with the default behavior. 71 | 72 | .. code-block:: javascript 73 | 74 | MauticFormCallback['replaceWithFormName'] = { 75 | onMessageSet: function (messageObject) { 76 | if ('error' == messageObject.type) { 77 | // do something custom 78 | } 79 | }, 80 | }; -------------------------------------------------------------------------------- /docs/form_hooks/response_hooks.rst: -------------------------------------------------------------------------------- 1 | Response hooks 2 | ############## 3 | 4 | .. js:method:: onResponse() 5 | 6 | Called prior to processing the Form submission's response, which handles actions like downloading a file, displaying a success message, and/or redirecting to another URL. 7 | 8 | :param object response: The response object may have the following defined: 9 | 10 | * ``response.download`` URL of a file to download 11 | * ``response.redirect`` URL to redirect to. 12 | * ``response.validationErrors`` Array of fields with validation errors. 13 | * ``response.errorMessage`` String with an error message. 14 | * ``response.success`` TRUE if the Form was submitted successfully. 15 | * ``response.successMessage`` String with a success message. 16 | 17 | :returns: bool|NULL|void Return TRUE to skip the default behavior for handling the response. 18 | 19 | .. code-block:: javascript 20 | 21 | MauticFormCallback['formname'] = { 22 | onResponse: function (response) { 23 | // do something custom 24 | }, 25 | }; 26 | 27 | .. js:method:: onResponseStart() 28 | 29 | Called prior to the default processing of the response. 30 | 31 | :param object response: The response object may have the following defined: 32 | 33 | * ``response.download`` URL of a file to download 34 | * ``response.redirect`` URL to redirect to. 35 | * ``response.validationErrors`` Array of fields with validation errors. 36 | * ``response.errorMessage`` String with an error message. 37 | * ``response.success`` TRUE if the Form was submitted successfully. 38 | * ``response.successMessage`` String with a success message. 39 | 40 | :returns: void 41 | 42 | .. code-block:: javascript 43 | 44 | MauticFormCallback['formname'] = { 45 | onResponseStart: function (response) { 46 | // do something custom 47 | }, 48 | }; 49 | 50 | .. Note:: This isn't called if an :js:meth:`onResponse` hook returns TRUE. 51 | 52 | .. js:method:: onResponseEnd() 53 | 54 | Called after to the default processing of the response. 55 | 56 | :param object response: The response object may have the following defined: 57 | 58 | * ``response.download`` URL of a file to download 59 | * ``response.redirect`` URL to redirect to. 60 | * ``response.validationErrors`` Array of fields with validation errors. 61 | * ``response.errorMessage`` String with an error message. 62 | * ``response.success`` TRUE if the Form was submitted successfully. 63 | * ``response.successMessage`` String with a success message. 64 | 65 | :returns: void 66 | 67 | .. code-block:: javascript 68 | 69 | MauticFormCallback['formname'] = { 70 | onResponseEnd: function (response) { 71 | // do something custom 72 | }, 73 | }; 74 | 75 | .. Note:: This isn't called if an :js:meth:`onResponse` hook returns TRUE or if the page redirects. -------------------------------------------------------------------------------- /docs/design/tiles.rst: -------------------------------------------------------------------------------- 1 | Using the Tile Component 2 | ######################## 3 | 4 | Overview 5 | ******** 6 | 7 | The Tile Component is a versatile, reusable UI element. It's designed to be flexible and adaptable, capable of serving a variety of functions in a User interface. The Tile Component displays content, acts as a clickable or selectable element, and houses an icon. 8 | 9 | It's designed to group related information in flexible containers and can contain text, images, and/or a block of color. The usage guidance for the Tile Component is intentionally high-level, focusing specifically on the tile's structure and responsiveness to the grid. It lacks pre-set styles for the content, allowing for the display of a wide variety of content. This flexibility makes tiles suitable for various use cases, such as displaying feature highlights, providing navigation options, or presenting data summaries, including information, getting started guides, how-to guides, and more. 10 | 11 | CSS class definitions 12 | ===================== 13 | 14 | The Tile Component consists of the following classes: 15 | 16 | ``.tile`` 17 | This is the base class for the Tile Component. It sets up the basic appearance and layout of the tile, including its size, padding, background color, and border radius. It also sets up some default styles for when the tile is in focus. 18 | 19 | ``.tile-clickable`` and ``.tile-selectable`` 20 | These classes are modifiers for the base ``.tile`` class. Use them to make the tile behave as a clickable or selectable element, respectively. These classes adjust the padding, border, margin, font properties, and cursor style of the tile. They also set up hover and focus styles. 21 | 22 | ``.tile-icon`` 23 | Use this class for an icon within the tile to position it and set its color. It also includes styles for a inactive icon. 24 | 25 | How to use the Tile Component 26 | ============================= 27 | 28 | To use the Tile Component, you need to add the ``.tile`` class to an HTML element. If you want the tile to be clickable or selectable, add the ``.tile-clickable`` or ``.tile-selectable`` class as well. If you want to include an icon in the tile, add an element with the ``.tile-icon`` class inside the tile. 29 | 30 | .. code-block:: html 31 | 32 |
    33 |

    Basic Tile Content

    34 |
    35 | 36 | 37 |

    Clickable Tile Content

    38 |
    39 | 40 | 41 |

    Selectable Tile Content

    42 |
    43 | 44 | 45 | Examples using the Tile Component 46 | --------------------------------- 47 | 48 | .. image:: images/tile.png 49 | :alt: Base tile used to display import file information 50 | 51 | .. image:: images/tile-clickable.png 52 | :alt: Example of a clickable tile used to create an expressive link 53 | 54 | .. note:: 55 | 56 | When a tile isn't clickable, you must use a ``
    `` tag. Use a ```` tag only when turning the tile into a clickable element. 57 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /docs/webhooks/events/form_on_submit.rst: -------------------------------------------------------------------------------- 1 | Form submit event 2 | ################# 3 | 4 | Triggered when a Contact submits a Mautic Form. 5 | 6 | .. _form_submit_event_type: 7 | 8 | Event type 9 | ********** 10 | 11 | ``mautic.form_on_submit`` 12 | 13 | .. _form_submit_event_properties: 14 | 15 | Event properties 16 | **************** 17 | 18 | .. list-table:: 19 | :header-rows: 1 20 | 21 | * - Key 22 | - Type 23 | - Description 24 | * - ``submission`` 25 | - object 26 | - :ref:`Submission object` 27 | * - ``timestamp`` 28 | - string 29 | - Date/time the event occurred in ISO 8601 format. 30 | 31 | Submission properties 32 | ********************* 33 | 34 | .. list-table:: 35 | :header-rows: 1 36 | 37 | * - Key 38 | - Type 39 | - Description 40 | * - ``id`` 41 | - int 42 | - ID of the Submission. 43 | * - ``ipAddress`` 44 | - object 45 | - :ref:`IP address basic object` for the Contact when they submitted the Form. 46 | * - ``form`` 47 | - object 48 | - :ref:`Form basic object`. 49 | * - ``lead`` 50 | - object 51 | - :ref:`Contact object`. 52 | * - ``trackingId`` 53 | - string 54 | - Generated ID for the tracking session. 55 | * - ``dateSubmitted`` 56 | - string 57 | - Date/time the submission occurred in ISO 8601 format. 58 | * - ``referer`` 59 | - string 60 | - URL the Form submitted from. 61 | * - ``results`` 62 | - object 63 | - Key/value pairs with Form field API names as the keys and submitted values. 64 | * - ``page`` 65 | - object|null 66 | - :ref:`Landing Page basic object` if the Contact submitted the Form when embedded on a Landing Page. Otherwise, ``null``. 67 | 68 | Form basic properties 69 | ********************* 70 | 71 | .. list-table:: 72 | :header-rows: 1 73 | 74 | * - Key 75 | - Type 76 | - Description 77 | * - ``id`` 78 | - int 79 | - ID of the Form 80 | * - ``name`` 81 | - string 82 | - Name of the Form 83 | * - ``alias`` 84 | - string 85 | - API name for the Form 86 | * - ``category`` 87 | - object 88 | - :ref:`Category object` 89 | 90 | IP address basic properties 91 | *************************** 92 | 93 | .. list-table:: 94 | :header-rows: 1 95 | 96 | * - ``ipAddress`` 97 | - string 98 | - IP address for the Contact. 99 | * - ``id`` 100 | - int 101 | - ID of the IP address stored in Mautic's database. 102 | * - ``ipDetails`` 103 | - object|null 104 | - Details of the IP address populated from the configured IP lookup service. -------------------------------------------------------------------------------- /docs/webhooks/events/company_post_save.rst: -------------------------------------------------------------------------------- 1 | Company created/updated event 2 | ############################# 3 | 4 | Triggered when Mautic creates or updates a Company. 5 | 6 | .. _company_created_updated_event_type: 7 | 8 | Event type 9 | ********** 10 | 11 | ``mautic.company_post_save`` 12 | 13 | .. _company_created_updated_event_properties: 14 | 15 | Event properties 16 | **************** 17 | 18 | .. list-table:: 19 | :header-rows: 1 20 | 21 | * - Key 22 | - Type 23 | - Description 24 | * - ``company`` 25 | - object 26 | - :ref:`Company object`. 27 | * - ``timestamp`` 28 | - string 29 | - Date/time the event occurred in ISO 8601 format. 30 | 31 | Company properties 32 | ****************** 33 | 34 | 35 | .. list-table:: 36 | :header-rows: 1 37 | 38 | * - Key 39 | - Type 40 | - Description 41 | * - ``id`` 42 | - int 43 | - ID of the Company 44 | * - ``isPublished`` 45 | - boolean 46 | - Always true. 47 | * - ``dateAdded`` 48 | - string 49 | - Date/time of Company creation in ISO 8601 format. 50 | * - ``createdBy`` 51 | - int|null 52 | - The ID of the User who created the Company or null if unknown. 53 | * - ``createdByUser`` 54 | - string|null 55 | - Name of the User that created the Company. 56 | * - ``dateModified`` 57 | - string|null 58 | - Date/time the Company was last modified in ISO 8601 format or null if not modified. 59 | * - ``modifiedBy`` 60 | - int|null 61 | - The ID of the User who last modified the Company or null if unknown. For example, visitor tracking. 62 | * - ``modifiedByUser`` 63 | - string|null 64 | - Name of the User that last modified the Company if applicable. Otherwise null. 65 | * - ``name`` 66 | - string 67 | - The Company's name. 68 | * - ``address1`` 69 | - string|null 70 | - The Company's address line 1. 71 | * - ``address2`` 72 | - string|null 73 | - The Company's address line 2. 74 | * - ``city`` 75 | - string|null 76 | - The Company's city. 77 | * - ``state`` 78 | - string 79 | - The Company's state. 80 | * - ``zipcode`` 81 | - string|null 82 | - The Company's zip code. 83 | * - ``country`` 84 | - string|null 85 | - The Company's country. 86 | * - ``phone`` 87 | - string|null 88 | - The Company's phone number. 89 | * - ``website`` 90 | - string|null 91 | - The Company's website. 92 | * - ``industry`` 93 | - string|null 94 | - The Company's industry. 95 | * - ``score`` 96 | - string|null 97 | - The Company's behavior score - similar to Contact Points. 98 | * - ``fields`` 99 | - object|array 100 | - Mautic groups fields by Field Groups keyed as one of ``core``, ``social``, ``personal``, and ``professional``. Each ``fieldGroup`` has an object of Fields keyed by the Field's API name. See :ref:`Custom Field object` for each Field's properties. Note that this could be an object if there are Fields available. Otherwise, Mautic sets an empty array. For example, ``$companyCity = $company['fields']['core']['city']['value'];``. -------------------------------------------------------------------------------- /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/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 | 'code_samples_ext', 49 | 'sphinx_rtd_theme', 50 | 'sphinx.ext.viewcode', 51 | 'sphinx.ext.autosectionlabel', 52 | 'sphinxcontrib.phpdomain', 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 | # GH Edit button 66 | 67 | html_context = { 68 | "display_github": True, # Integrate GitHub 69 | "github_user": "mautic", # Username 70 | "github_repo": "developer-documentation-new", # Repository name 71 | "github_version": "5.x", # Branch name 72 | "conf_py_path": "/docs/", # Path in the repository to conf.py 73 | } 74 | 75 | # -- Options for HTML output ------------------------------------------------- 76 | 77 | # The theme to use for HTML and HTML Help pages. See the documentation for 78 | # a list of builtin themes. 79 | # 80 | html_theme = 'sphinx_rtd_theme' 81 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 82 | 83 | # Add any paths that contain custom static files (such as style sheets) here, 84 | # relative to this directory. They are copied after the builtin static files, 85 | # so a file named "default.css" will overwrite the builtin "default.css". 86 | html_static_path = ['_static'] 87 | html_css_files = ['tablefix.css'] 88 | 89 | 90 | # Please add links here that do not pass the "make checklinks" check. 91 | # A little context on the reason for ignoring is greatly appreciated! 92 | linkcheck_ignore = [ 93 | # github anchors cause failures, so do not check any github url with an anchor 94 | r'^https://github.com/.*#.*' 95 | ] 96 | 97 | # Ensure that autosectionlabel will produce unique names 98 | autosectionlabel_prefix_document = True 99 | # autosectionlabel_maxdepth = 1 100 | -------------------------------------------------------------------------------- /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/webhooks/example_scripts.rst: -------------------------------------------------------------------------------- 1 | Webhook example scripts 2 | ####################### 3 | 4 | PHP 5 | *** 6 | 7 | .. code-block:: php 8 | 9 | format('Y-m-d H:i:s') . ' ' . $message . "\n\n", 3, $prefix . $file); 28 | } 29 | 30 | /** 31 | * Get the request JSON object and log the request 32 | */ 33 | public function getRequest(): string 34 | { 35 | $rawData = file_get_contents("php://input"); 36 | 37 | $this->log($rawData, 'request'); 38 | 39 | return $rawData; 40 | } 41 | } 42 | 43 | $secret = 'mySuperWebhookSecretKey'; 44 | $webhook = new webhookTest; 45 | $rawData = $webhook->getRequest(); 46 | 47 | // optional signature verification 48 | $headers = getallheaders(); 49 | $receivedSignature = $headers['Webhook-Signature']; 50 | $computedSignature = base64_encode(hash_hmac('sha256', $rawData, $secret, true)); 51 | 52 | if ($receivedSignature === $computedSignature) { 53 | $webhook->log('Webhook authenticity verification OK', 'request'); 54 | } else { 55 | $webhook->log('Webhook not authentic!', 'request'); 56 | } 57 | 58 | // @todo Process the $requestData as needed 59 | $requestData = json_decode($rawData); 60 | 61 | if (isset($requestData['mautic.lead_post_save_new'])) { 62 | foreach ($requestData['mautic.lead_post_save_new'] as $contact) { 63 | // do something with the newly identified Contacts 64 | } 65 | } 66 | 67 | Node.js 68 | ******* 69 | 70 | .. code-block:: javascript 71 | 72 | 'use strict'; 73 | 74 | const express = require('express'); 75 | const crypto = require('crypto'); 76 | const app = express(); 77 | const port = 3000; 78 | const SECRET = 'mySecret'; 79 | 80 | // save raw body 81 | app.use ((req, res, next) => { 82 | let data = ''; 83 | req.setEncoding('utf8'); 84 | 85 | req.on('data', chunk => data += chunk); 86 | req.on('end', () => { 87 | req.body = data; 88 | return next(); 89 | }); 90 | }); 91 | 92 | app.post('/webhook', (req, res) => { 93 | 94 | // optional signature verification 95 | const receivedSignature = req.headers['webhook-signature']; 96 | console.log('Received signature (in header):', receivedSignature); 97 | 98 | const computedSignature = crypto.createHmac('sha256', SECRET).update(req.body).digest('base64'); 99 | console.log('Computed signature (from body):', computedSignature); 100 | 101 | if (receivedSignature === computedSignature) { 102 | console.log('Webhook authenticity verification OK'); 103 | } else { 104 | console.log('Webhook not authentic!'); 105 | } 106 | 107 | // TODO: process body 108 | const body = JSON.parse(req.body); 109 | 110 | if (body["mautic.lead_post_save_new"].length) { 111 | // do something with the array of newly identified Contacts 112 | } 113 | 114 | res.send(); 115 | }); 116 | 117 | app.listen(port, () => console.log(`App listening on port ${port}!`)); 118 | -------------------------------------------------------------------------------- /docs/themes/legacy.rst: -------------------------------------------------------------------------------- 1 | Legacy Builder 2 | ############## 3 | 4 | .. warning:: The Legacy Builder is still available in Mautic 4 but planned to be deleted in the future. Refer to the :xref:`Builder documentation` for more information. 5 | 6 | Slots 7 | ***** 8 | 9 | Slot definition 10 | =============== 11 | 12 | Define the slot with a the HTML attribute, ```data-slot="{slot type here}"````. For example, the text slot can be defined even with the demo content. 13 | 14 | The div with the attribute, ```data-slot="text"```, makes the text inside the div editable within the inline Froala editor when opening the builder. 15 | 16 | .. code-block:: html 17 | 18 |
    19 | @JaneDoe has invited you to join Awesome inc! 20 |
    21 | 22 | The slot types currently built: 23 | 24 | Image 25 | ----- 26 | Inserts a single image into the div. You can click and edit it with options which provides Froala editor. 27 | 28 | Button 29 | ------ 30 | Inserts an HTML button. You can define text, URL as well as padding, size, and position. 31 | 32 | Text 33 | ---- 34 | Inserts a new text slot which you can edit with a HTML editor. This enables the use of media such as images and videos. 35 | 36 | Separator 37 | --------- 38 | Inserts a horizontal line to separate content. 39 | 40 | Slot containers 41 | =============== 42 | As previously stated, the User can drag and drop new slots when creating an Email based on a Theme. Therefore, as a Theme developer, you have to define where the User can drop the slots. You can do this with a single HTML attribute ``data-slot-container="1"``. 43 | 44 | .. code-block:: html 45 | 46 |
    47 |
    48 | @JaneDoe has invited you to join Awesome inc! 49 |
    50 |
    51 | 52 | This enables the User to drop the new slots into this container. In the example preceding, there is already one predefined slot which the User can move to another container, remove, or edit. 53 | 54 | This capability provides you with lots of creative freedom for designing and developing your own unique Email and Landing Pages. Have a unique design? Share it with the community on the forums. The community would love to see how you’re using Mautic to engage your audience. 55 | 56 | Sections 57 | ======== 58 | 59 | Sections are full width parts of the Theme which can enable the User to change the background color in the section wrapper, full monitor width, and in the section content itself. It's possible to move the sections up or down, delete the sections, and even create a new ones with layout of 1,2 or 3 columns. 60 | 61 | Section 62 | ------- 63 | 64 | The section holds and wraps the content. It can be any block HTML element with attribute ``data-section="1"``. 65 | 66 | .. note:: Center and add a fixed width consistent with all other sections to the element. 67 | 68 | .. code-block:: html 69 | 70 |
    71 |
    72 |
    73 | @JaneDoe has invited you to join Awesome inc! 74 |
    75 |
    76 |
    77 | 78 | Section wrapper 79 | --------------- 80 | 81 | Section wrappers must have 100% width of the browser window. You therefore have to split your Theme into several "rows" if you want to enable the Users to change the background of each section. The section wrapper can be any block HTML element with attribute ``data-section-wrapper="1"``. 82 | 83 | .. code-block:: html 84 | 85 |
    86 |
    87 |
    88 |
    89 | @JaneDoe has invited you to join Awesome inc! 90 |
    91 |
    92 |
    93 |
    94 | -------------------------------------------------------------------------------- /docs/form_hooks/validation_hooks.rst: -------------------------------------------------------------------------------- 1 | Validation hooks 2 | ################ 3 | 4 | .. js:method:: onValidate() 5 | 6 | Called prior to default Form validation. This is useful if you want to execute custom code prior to the Form being submitted, or if you want to do your own validation. 7 | 8 | :returns: bool|NULL|void Return TRUE to skip the default validation and continue with submitting the Form. Return FALSE to skip the default validation and prevent the Form submission. Return NULL to still proceed with default validation. 9 | 10 | .. code-block:: javascript 11 | 12 | MauticFormCallback['formname'] = { 13 | onValidate: function () { 14 | var email = document.getElementById('mauticform_input_formname1_email').value; 15 | if (email.includes('@gmail.com')) { 16 | alert('Please use a work email address.'); 17 | 18 | // return FALSE to stop the form submission 19 | return FALSE; 20 | } 21 | 22 | // return NULL|void to continue with default validation or return TRUE to skip it 23 | }, 24 | }; 25 | 26 | .. js:method:: onValidateStart() 27 | 28 | Called at the beginning of the default Form validation. 29 | 30 | :returns: void 31 | 32 | .. code-block:: javascript 33 | 34 | MauticFormCallback['formname'] = { 35 | onValidateStart: function () { 36 | // do some custom stuff 37 | }, 38 | }; 39 | 40 | .. Note:: This isn't called if an :js:meth:`onValidate` hook returns TRUE. 41 | 42 | 43 | .. js:method:: onValidateEnd() 44 | 45 | Called after the Form has been validated by either the default validation or the onValidate hook. 46 | 47 | :param bool isFormValid: TRUE if the Form was successfully validated or FALSE if not. 48 | :returns: void 49 | 50 | .. code-block:: javascript 51 | 52 | MauticFormCallback['formname'] = { 53 | onValidateEnd: function (isFormValid) { 54 | // do some custom stuff 55 | }, 56 | }; 57 | 58 | .. js:method:: onErrorMark() 59 | 60 | Called before updating a field's element with a validation error. 61 | 62 | :param object fieldValidationObject: 63 | * ``fieldValidationObject.containerId`` The ID of the field's container element. 64 | * ``fieldValidationObject.valid`` TRUE|FALSE 65 | * ``fieldValidationObject.validationMessage`` The error message. 66 | 67 | :returns: bool|NULL|void Return TRUE to skip the default behavior of appending the validation message to the field container's element with the ``.mauticform-errormsg`` class. 68 | 69 | .. code-block:: javascript 70 | 71 | var fieldValidationObject = { 72 | containerId: 'mauticform_formname_email', 73 | valid: FALSE, 74 | validationMessage: 'Email is required!' 75 | }; 76 | 77 | MauticFormCallback['formname'] = { 78 | onErrorMark: function (fieldValidationObject) { 79 | if ('mauticform_formname_email' == fieldValidationObject.containerId && !fieldValidationObject.valid) { 80 | // do something custom 81 | } 82 | }, 83 | }; 84 | 85 | .. js:method:: onErrorClear() 86 | 87 | Called prior to clearing a field's validation error. 88 | 89 | :param string fieldContainerId: The ID of the field's container element. 90 | 91 | :returns: bool|NULL|void Return TRUE to skip the default behavior of clearing the validation message from the field container's element with the ``.mauticform-errormsg`` class. 92 | 93 | .. code-block:: javascript 94 | 95 | MauticFormCallback['formname'] = { 96 | onErrorClear: function (fieldContainerId) { 97 | if ('mauticform_formname_email' == fieldContainerId) { 98 | // do something custom 99 | } 100 | }, 101 | }; 102 | -------------------------------------------------------------------------------- /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/marketplace/listing.rst: -------------------------------------------------------------------------------- 1 | Listing a Plugin in the Marketplace 2 | ################################### 3 | 4 | .. note:: 5 | Do you already have a Plugin that you developed for previous versions of Mautic? Please see the :ref:`marketplace/listing:Updating existing Plugins for usage with the Marketplace` section below. 6 | 7 | Preparing your Plugin for the Marketplace 8 | ***************************************** 9 | 10 | .. warning:: 11 | It's critical that you follow the steps below carefully. Things like typos in the bundle name can crash Mautic and require users to issue some command line commands to get back into a working state. 12 | 13 | When you create a Plugin, there's a few requirements to verify before listing it in the Mautic Marketplace. 14 | 15 | Step 1: checking your ``composer.json`` file 16 | ============================================ 17 | 18 | The first important step is to validate whether your ``composer.json`` file has all the required information in it. 19 | 20 | Here's an example ``composer.json`` file: 21 | 22 | .. code-block:: json 23 | 24 | { 25 | "name": "example-vendor/plugin-example", 26 | "description": "Example Plugin", 27 | "type": "mautic-plugin", 28 | "license": "GPL-3.0-or-later", 29 | "keywords": ["mautic","plugin","integration"], 30 | "extra": { 31 | "install-directory-name": "ExampleBundle" 32 | }, 33 | "require": { 34 | "php": ">=8.0 <8.1", 35 | "ext-zip": "^1.15", 36 | "mautic/core-lib": "^5.0" 37 | } 38 | } 39 | 40 | In addition to the information you already have in the ``composer.json`` for your Plugin, please add: 41 | 42 | - ``type`` must have value ``mautic-plugin``. The Marketplace is filtering PHP packages by this tag. It's required to show up in the Marketplace. 43 | - ``extra.install-directory-name`` specifies the directory name for the bundle. Correct directory name is important for PSR4 autoloading. It must be the same as in the namespace in your PHP classes. 44 | - ``require.php`` be sure to specify the PHP version range that you test the Plugin against. Ideally it should be the same as the Mautic's :xref:`Mautic supported PHP versions`. Don't allow users to install the Plugin on versions that aren't supported. 45 | - ``require.ext-*`` if your Plugin require some PHP extension, please list them in the require section too. As servers can have a wide variety of extensions enabled, this ensures that the correct extensions are available. 46 | - ``require.mautic/core-lib`` it's important to specify which Mautic versions your Plugin supports. Include only the versions you or your community have tested and confirmed the Plugin works with. 47 | 48 | Step 2: verify best practices 49 | ============================= 50 | 51 | There's a few things which are highly recommend when creating Plugins for Mautic. Please refer to the :doc:`./best_practices` page for more details. 52 | 53 | Step 3: publish your package 54 | ============================ 55 | 56 | When the ``composer.json`` is ready, follow the :xref:`Packagist` section directly in Packagist. 57 | 58 | Step 4: apply for the allowlist 59 | =============================== 60 | 61 | While the Mautic Marketplace is still in beta, you'll have to apply for the allowlist to show up in the Marketplace. Please refer to the :doc:`./allowlist_what_and_why` page for more details. 62 | 63 | Updating existing Plugins for usage with the Marketplace 64 | ******************************************************** 65 | 66 | If you required ``mautic/composer-plugin`` in your plugin's dependencies in the past, please remove it. Support for Mautic plugins is now built into Composer, so you only have to set the type to ``mautic-plugin`` and Composer will automatically install your plugin into the ``plugins`` folder. 67 | 68 | Next to that, if you built your Plugin for Mautic 4 originally, please read the :xref:`UPGRADE_GUIDE` for the breaking changes in Mautic 5. 69 | 70 | When you're done, you can go back to the :ref:`marketplace/listing:Preparing your plugin for the Marketplace` section in this document and proceed from there. 71 | -------------------------------------------------------------------------------- /docs/components/integrations_sync.rst: -------------------------------------------------------------------------------- 1 | .. It is a reference only page, not a part of doc tree. 2 | 3 | :orphan: 4 | 5 | .. vale off 6 | 7 | Sync engine 8 | ########### 9 | 10 | .. vale on 11 | 12 | The Sync Engine supports bidirectional syncing between Mautic's Contact and Companies with third party objects. The engine generates a "``sync report``" from Mautic that it converts to a "``sync order``" for the Integration to process. It then asks for a "``sync report``" from the Integration which it converts to a "``sync order``" for Mautic to process. 13 | 14 | When building the Report, Mautic or the Integration fetches the objects those either modified or created within the specified timeframe. If the Integration supports changes at the field level, it should tell the Report on a per-field basis when the field was last updated. Otherwise, it should tell the Report when the object itself was last modified. The "``sync judge``" uses these dates to determine which value to use in bi-directional sync. 15 | 16 | The ``mautic:integrations:sync`` command initiates the sync. For example:: 17 | 18 | php bin/console mautic:integrations:sync HelloWorld --start-datetime="2020-01-01 00:00:00" --end-datetime="2020-01-02 00:00:00". 19 | 20 | ------ 21 | 22 | .. vale off 23 | 24 | Registering the Integration for the Sync Engine 25 | *********************************************** 26 | 27 | .. vale on 28 | 29 | To tell the IntegrationsBundle that this Integration provides a syncing feature, tag the Integration or support class with ``mautic.sync_integration`` in the Plugin's ``app/config.php``. 30 | 31 | .. code-block:: php 32 | 33 | [ 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/webhooks/events/page_on_hit.rst: -------------------------------------------------------------------------------- 1 | Page hit event 2 | ############## 3 | 4 | Triggered when a Contact visits a Landing Page or Mautic tracked third party webpage. 5 | 6 | .. _page_on_hit_event_type: 7 | 8 | Event type 9 | ********** 10 | 11 | ``mautic.page_on_hit`` 12 | 13 | .. _page_on_hit_event_properties: 14 | 15 | Event properties 16 | **************** 17 | 18 | .. list-table:: 19 | :header-rows: 1 20 | 21 | * - Key 22 | - Type 23 | - Description 24 | * - ``hit`` 25 | - object 26 | - :ref:`Page visit object` 27 | * - ``timestamp`` 28 | - string 29 | - Date/time the event occurred in ISO 8601 format. 30 | 31 | Page visit properties 32 | ********************* 33 | 34 | .. list-table:: 35 | :header-rows: 1 36 | 37 | * - Key 38 | - Type 39 | - Description 40 | * - ``id`` 41 | - int 42 | - ID of the Page visit. 43 | * - ``dateHit`` 44 | - string 45 | - Date/time the visit occurred in ISO 8601 format. 46 | * - ``dateHit`` 47 | - null 48 | - Always ``null`` for Webhook events. 49 | * - ``page`` 50 | - object 51 | - :ref:`Landing Page basic object`. 52 | * - ``lead`` 53 | - object 54 | - :ref:`Contact object`. 55 | * - ``ipAddress`` 56 | - object 57 | - :ref:`IP address basic object` for the Contact when they visited the Page. 58 | * - ``country`` 59 | - string 60 | - Country gleaned from tracked IP address. 61 | * - ``region`` 62 | - string 63 | - Region gleaned from tracked IP address. 64 | * - ``city`` 65 | - string 66 | - City gleaned from tracked IP address. 67 | * - ``isp`` 68 | - string 69 | - ISP gleaned from tracked IP address. 70 | * - ``organization`` 71 | - string 72 | - Organization gleaned from tracked IP address. 73 | * - ``code`` 74 | - int 75 | - Response code from the Page visit. 76 | * - ``referer`` 77 | - string 78 | - Referrer for the Page visit. 79 | * - ``url`` 80 | - string 81 | - URL for the Page visit. 82 | * - ``urlTitle`` 83 | - string 84 | - Page title for the visited the tracked URL. 85 | * - ``userAgent`` 86 | - string 87 | - User agent for the browser that visited the tracked URL. 88 | * - ``remoteHost`` 89 | - string|null 90 | - Fully qualified domain name of the client sending the request to the server. 91 | * - ``pageLanguage`` 92 | - string 93 | - Locale configured for the Landing Page assuming the visit was to a Landing Page. 94 | * - ``browserLanguages`` 95 | - array 96 | - Array of languages as configured in the browser. 97 | * - ``trackingId`` 98 | - string 99 | - Generated ID for the tracking session. 100 | * - ``source`` 101 | - string 102 | - Source that generated the URL if known. For example, ``email`` or ``sms``. 103 | * - ``sourceId`` 104 | - mixed 105 | - ID of the specific source that generated the URL if known. For example, ``source`` equaling ``email`` and ``sourceID`` equaling 1 means that Email ID 1 generated the URL that the Contact clicked. 106 | * - ``query`` 107 | - array 108 | - Key/value pair of query parameters for the visited URL. 109 | * - ``redirect`` 110 | - object|null 111 | - 112 | * - ``email`` 113 | - object 114 | - Deprecated. Use ``source`` and ``sourceId`` where ``source`` equals ``email``. 115 | 116 | .. vale off 117 | 118 | Landing Page basic properties 119 | ***************************** 120 | 121 | .. vale on 122 | 123 | .. list-table:: 124 | :header-rows: 1 125 | 126 | * - Key 127 | - Type 128 | - Description 129 | * - ``id`` 130 | - int 131 | - ID of the Landing Page. 132 | * - ``title`` 133 | - string 134 | - Name of the Landing Page. 135 | * - ``alias`` 136 | - string 137 | - API name for the Landing Page. 138 | * - ``category`` 139 | - object 140 | - :ref:`Category object`. -------------------------------------------------------------------------------- /docs/plugins/installation.rst: -------------------------------------------------------------------------------- 1 | Installing, upgrading, and uninstalling 2 | ####################################### 3 | 4 | Mautic informs your Plugin when it gets installed or updated through the ``ON_PLUGIN_INSTALL`` and ``ON_PLUGIN_UPDATE`` events. This can be useful in scenarios where you need to set up certain data structures or do other configuration work. Note that there is currently no hook for when your Plugin gets uninstalled. If that's of interest, please feel free to :xref:`contribute that feature`. 5 | 6 | .. note:: If your Plugin manages its own schema, Mautic recommends using :ref:`plugins/installation:database migrations` instead of the generic events mentioned earlier. 7 | 8 | Install and update events 9 | ========================= 10 | 11 | .. note:: The events below are available since Mautic 4.2.0. 12 | 13 | You can create event listeners as follows: 14 | 15 | .. code-block:: php 16 | 17 | ['onPluginInstall', 0], 34 | PluginEvents::ON_PLUGIN_UPDATE => ['onPluginUpdate', 0], 35 | ]; 36 | } 37 | 38 | public function onPluginInstall(PluginInstallEvent $event) 39 | { 40 | // Handle your logic here 41 | } 42 | 43 | public function onPluginUpdate(PluginUpdateEvent $event) 44 | { 45 | // Handle your logic here 46 | } 47 | } 48 | 49 | Database migrations 50 | =================== 51 | 52 | Mautic supports database migrations for Plugins to better manage their schema. Queries are in migration files that match the Plugin's version number in its config. When a Plugin gets installed or upgraded, Mautic loops over the migration files up to the latest version. 53 | 54 | Check your Plugin's root bundle class 55 | ------------------------------------- 56 | 57 | The Plugin's root bundle class should extend ``MauticPlugin\IntegrationsBundle\Bundle\AbstractPluginBundle``: 58 | 59 | .. code-block:: php 60 | 61 | getTable($this->concatPrefix($this->table))->hasColumn('is_enabled'); 100 | } catch (SchemaException $e) { 101 | return false; 102 | } 103 | } 104 | 105 | protected function up(): void 106 | { 107 | $this->addSql("ALTER TABLE `{$this->concatPrefix($this->table)}` ADD `is_enabled` tinyint(1) 0"); 108 | 109 | $this->addSql("CREATE INDEX {$this->concatPrefix('is_enabled')} ON {$this->concatPrefix($this->table)}(is_enabled);"); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /docs/design/quick_filters.rst: -------------------------------------------------------------------------------- 1 | Quick filters for searches 2 | ########################## 3 | 4 | Quick filters provide an efficient way to search using existing search commands. This documentation outlines how to create and implement new quick filters for Mautic searches. 5 | 6 | Implementation overview 7 | *********************** 8 | 9 | Mautic implements quick filters using a combination of JavaScript and Twig templates. The process involves three main components: 10 | 11 | 1. JavaScript function for applying the filter 12 | 2. Twig template for rendering filter buttons 13 | 3. Array of PHP code for defining filter options 14 | 15 | JavaScript functionality 16 | ======================== 17 | 18 | The ``Mautic.listQuickFilter`` function is responsible for applying the quick filter: 19 | 20 | .. code-block:: javascript 21 | 22 | Mautic.listQuickFilter = function (element) { 23 | const filterValue = element.dataset.filter; 24 | const searchInput = document.getElementById('list-search'); 25 | searchInput.value = filterValue; 26 | const enterKeyEvent = new KeyboardEvent('keyup', { 27 | keyCode: 13 28 | }); 29 | searchInput.dispatchEvent(enterKeyEvent); 30 | } 31 | 32 | This function performs the following actions: 33 | 34 | 1. Retrieves the filter value from the clicked element's ``data-filter`` attribute 35 | 2. Sets the search input field's value to the filter value 36 | 3. Simulates an Enter key press to trigger the search 37 | 38 | Twig template 39 | ============= 40 | 41 | Mautic renders the quick filter buttons using a Twig template: 42 | 43 | .. code-block:: twig 44 | 45 | {% if quickFilters is defined and quickFilters is not empty %} 46 |
    47 | {% for quickFilter in quickFilters %} 48 | 54 | 55 | {{ quickFilter.label|trans }} 56 | 57 | {% endfor %} 58 |
    59 | {% endif %} 60 | 61 | This template iterates through the provided quick filters and creates clickable labels for each one on the toolbar. 62 | 63 | Implementing quick filters 64 | ========================== 65 | 66 | To add quick filters to a list view, include the ``list_toolbar.html.twig`` template and pass the ``quickFilters`` option: 67 | 68 | .. code-block:: twig 69 | 70 | {{ include('@MauticCore/Helper/list_toolbar.html.twig', { 71 | 'searchValue': searchValue, 72 | 'searchHelp': 'mautic.form.form.help.searchcommands', 73 | 'searchId': 'list-search', 74 | 'action': currentRoute, 75 | 'quickFilters': [ 76 | { 77 | 'search': 'has:results', 78 | 'label': 'mautic.core.search.quickfilter.form_results', 79 | 'tooltip': 'mautic.core.search.quickfilter.form_results.tooltip', 80 | 'icon': 'ri-file-list-2-line' 81 | } 82 | ] 83 | }) }} 84 | 85 | Quick filter options 86 | ==================== 87 | 88 | You define each quick filter as an associative array with the following keys: 89 | 90 | - ``search``: the search query to apply 91 | - ``label``: the displayed text for the filter button 92 | - ``tooltip``: the tooltip text shown on hover 93 | - ``icon``: the CSS class for the icon displayed on the button 94 | 95 | Multiple quick filters 96 | ====================== 97 | 98 | You can define multiple quick filters by adding more items to the ``quickFilters`` array: 99 | 100 | .. code-block:: php 101 | 102 | 'quickFilters': [ 103 | { 104 | 'search': 'is:admin', 105 | 'label': 'mautic.user.role.form.isadmin', 106 | 'tooltip': 'mautic.core.search.quickfilter.is_admin', 107 | 'icon': 'ri-admin-line' 108 | }, 109 | { 110 | 'search': 'is:published', 111 | 'label': 'mautic.core.form.active', 112 | 'tooltip': 'mautic.core.search.quickfilter.is_published', 113 | 'icon': 'ri-check-line' 114 | }, 115 | { 116 | 'search': 'is:unpublished', 117 | 'label': 'mautic.core.form.inactive', 118 | 'tooltip': 'mautic.core.search.quickfilter.is_unpublished', 119 | 'icon': 'ri-close-line' 120 | } 121 | ] 122 | -------------------------------------------------------------------------------- /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 | 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 | 'cache_adapter_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 | 'cache_adapter_redis' => [ 107 | 'dsn' => 'redis://localhost:6379/0', 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/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 ``
      `` where each item ``
    • `` represents an accordion panel. Each panel consists of a heading and a collapsible content section. 14 | 15 | Key features 16 | ============ 17 | 18 | - Clicking on the heading expands or collapses each accordion item. 19 | - The component includes ARIA attributes to improve accessibility. 20 | - You can customize the content of each accordion panel using Twig variables. 21 | 22 | Applying the accordion component 23 | ================================ 24 | 25 | To use the accordion component, include it in your template and pass the necessary data. 26 | 27 | Define the content you want to include in the accordion. For example, if you want to include a group of UTM tags fields, you can define the content as follows: 28 | 29 | .. code-block:: twig 30 | 31 | {% set utmTagsContent %} 32 | {% for i, utmTag in form.utmTags %} 33 | {{ form_row(utmTag) }} 34 | {% endfor %} 35 | {% endset %} 36 | 37 | .. vale off 38 | 39 | .. note:: 40 | For instance, you can loop over Form fields or any other data to generate the content dynamically. 41 | 42 | .. vale on 43 | 44 | Include the ``accordion.html.twig`` template in your main template and pass an array of items. Each item should have: 45 | 46 | - ``id``: a unique identifier. 47 | - ``title``: the title of the accordion item. 48 | - ``padding_inline``: optional boolean to control padding within the content. Defaults to true if not defined. 49 | - ``content``: the content displays when the item expands. 50 | 51 | Example: 52 | 53 | .. code-block:: twig 54 | 55 | {% include '@MauticCore/Helper/accordion.html.twig' with { 56 | 'items': [ 57 | { 58 | 'id': 'UTM', 59 | 'title': 'mautic.email.utm_tags', 60 | 'padding_inline': false, 61 | 'content': utmTagsContent, 62 | } 63 | ] 64 | } %} 65 | 66 | While defining a ``set`` block separately isn't strictly necessary, it can be helpful to ensure that operations relying on Twig functions keep working correctly. The ``set`` block allows you to predefine complex content or operations, making your template cleaner and more maintainable. 67 | 68 | Example without the ``set`` block 69 | --------------------------------- 70 | 71 | If your content is simple, you can directly include it within the ``items`` array without using a ``set`` block. Here's an example: 72 | 73 | .. code-block:: twig 74 | 75 | {% include '@MauticCore/Helper/accordion.html.twig' with { 76 | 'items': [ 77 | { 78 | 'id': 'Example', 79 | 'title': 'Example Title', 80 | 'padding_inline': true, 81 | 'content': '

      This is a simple content example.

      ', 82 | } 83 | ] 84 | } %} 85 | 86 | This flexibility allows you to choose the best approach based on each case. 87 | 88 | Automatic CSS handling 89 | ====================== 90 | 91 | When using the Component, all necessary CSS styles are automatically handled for you. This means that the Component comes pre-styled with classes such as ``accordion-heading``, ``accordion-wrapper``, and ``accordion-content``, ensuring a consistent and visually appealing appearance out of the box. 92 | 93 | - The Component includes predefined CSS classes that manage the layout, spacing, and interactive elements of the accordion. 94 | - You don't need to add any extra CSS to make the accordion function and look visually appealing. 95 | - It uses the existing Bootstrap function for collapsing panels. 96 | - Avoid overriding these classes in your own CSS. 97 | 98 | The design of the accordion makes it easy to implement, with all essential CSS styles already in place. This allows you to focus on integrating and using the component without worrying about additional styling. 99 | 100 | Complete example 101 | ================ 102 | 103 | Here is a complete example that demonstrates how to use the accordion Component in a Mautic template: 104 | 105 | .. code-block:: twig 106 | 107 | {% set utmTagsContent %} 108 | {% for i, utmTag in form.utmTags %} 109 | {{ form_row(utmTag) }} 110 | {% endfor %} 111 | {% endset %} 112 | 113 | {% include '@MauticCore/Helper/accordion.html.twig' with { 114 | 'items': [ 115 | { 116 | 'id': 'UTM', 117 | 'title': 'mautic.email.utm_tags', 118 | 'padding_inline': false, 119 | 'content': utmTagsContent, 120 | } 121 | ] 122 | } %} 123 | 124 | For more complex structures, with dozens of accordion items, you might prefer to copy the structure and build something unique, but the best approach would be to place each content under a set block. --------------------------------------------------------------------------------