├── .all-contributorsrc ├── .ddev ├── config.yaml ├── local.config.php.dist ├── mautic-local.php.dist └── mautic-setup.sh ├── .github ├── ci-files │ ├── .env │ ├── local.config.php │ ├── local_4.php │ └── local_5.php └── workflows │ └── tests.yml ├── .gitignore ├── .php-cs-fixer.php ├── .well-known └── funding-manifest-urls ├── LICENSE.md ├── README.md ├── UPGRADE-4.0.md ├── apitester ├── endpoints │ ├── Assets.json │ ├── Campaigns.json │ ├── Companies.json │ ├── Contacts.json │ ├── Data.json │ ├── Emails.json │ ├── Focus.json │ ├── Forms.json │ ├── Notifications.json │ ├── Pages.json │ ├── Points.json │ ├── Reports.json │ ├── Segments.json │ ├── Smses.json │ ├── Stages.json │ └── Users.json ├── includes │ ├── bootstrap │ │ ├── css │ │ │ ├── bootstrap-theme.css │ │ │ ├── bootstrap-theme.css.map │ │ │ ├── bootstrap-theme.min.css │ │ │ ├── bootstrap.css │ │ │ ├── bootstrap.css.map │ │ │ └── bootstrap.min.css │ │ ├── fonts │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.svg │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ └── glyphicons-halflings-regular.woff │ │ └── js │ │ │ ├── bootstrap.js │ │ │ └── bootstrap.min.js │ ├── jquery-1.11.1.min.js │ ├── style.css │ └── typeahead.js └── index.php ├── composer.json ├── lib ├── Api │ ├── Api.php │ ├── Assets.php │ ├── CampaignEvents.php │ ├── Campaigns.php │ ├── Categories.php │ ├── Companies.php │ ├── CompanyFields.php │ ├── ContactFields.php │ ├── Contacts.php │ ├── Data.php │ ├── Devices.php │ ├── DynamicContents.php │ ├── Emails.php │ ├── Files.php │ ├── Focus.php │ ├── Forms.php │ ├── Messages.php │ ├── Notes.php │ ├── Notifications.php │ ├── Pages.php │ ├── PointGroups.php │ ├── PointTriggers.php │ ├── Points.php │ ├── Reports.php │ ├── Roles.php │ ├── Segments.php │ ├── Smses.php │ ├── Stages.php │ ├── Stats.php │ ├── Tags.php │ ├── Themes.php │ ├── Tweets.php │ ├── Users.php │ └── Webhooks.php ├── Auth │ ├── AbstractAuth.php │ ├── ApiAuth.php │ ├── AuthInterface.php │ ├── BasicAuth.php │ └── OAuth.php ├── Exception │ ├── AbstractApiException.php │ ├── ActionNotSupportedException.php │ ├── AuthorizationRequiredException.php │ ├── ContextNotFoundException.php │ ├── IncorrectParametersReturnedException.php │ ├── RequiredParameterMissingException.php │ └── UnexpectedResponseFormatException.php ├── MauticApi.php ├── QueryBuilder │ ├── QueryBuilder.php │ └── WhereBuilder.php └── Response.php ├── phpstan.neon ├── phpunit.xml.dist └── tests ├── Api ├── AbstractCustomFieldsTest.php ├── AssetsTest.php ├── Auth │ ├── AbstractAuthTest.php │ └── BasicAuthTest.php ├── CampaignsTest.php ├── CategoriesTest.php ├── CompaniesTest.php ├── CompanyFieldTest.php ├── ContactFieldsTest.php ├── ContactsTest.php ├── DataTest.php ├── DevicesTest.php ├── DynamicContentsTest.php ├── EmailsTest.php ├── ExceptionsTest.php ├── FilesTest.php ├── FocusTest.php ├── FormsTest.php ├── MauticApiTestCase.php ├── MessagesTest.php ├── NotesTest.php ├── NotificationsTest.php ├── PagesTest.php ├── PointGroupsTest.php ├── PointTriggersTest.php ├── PointsTest.php ├── ReportsTest.php ├── ResponseInfoTest.php ├── RolesTest.php ├── SegmentsTest.php ├── SmsesTest.php ├── StagesTest.php ├── StatsTest.php ├── TagsTest.php ├── ThemesTest.php ├── TweetsTest.php ├── UsersTest.php ├── UtmTagsTest.php └── WebhooksTest.php ├── QueryBuilder └── WhereBuilderTest.php ├── ResponseTest.php ├── bootstrap.php ├── local.config.php.dist └── mauticlogo.png /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "kuzmany", 10 | "name": "Zdeno Kuzmany", 11 | "avatar_url": "https://avatars.githubusercontent.com/u/462477?v=4", 12 | "profile": "https://webmecanik.com", 13 | "contributions": [ 14 | "code" 15 | ] 16 | }, 17 | { 18 | "login": "dlopez-akalam", 19 | "name": "dlopez-akalam", 20 | "avatar_url": "https://avatars.githubusercontent.com/u/6641589?v=4", 21 | "profile": "https://github.com/dlopez-akalam", 22 | "contributions": [ 23 | "code" 24 | ] 25 | }, 26 | { 27 | "login": "mollux", 28 | "name": "mollux", 29 | "avatar_url": "https://avatars.githubusercontent.com/u/3983285?v=4", 30 | "profile": "https://github.com/mollux", 31 | "contributions": [ 32 | "code" 33 | ] 34 | }, 35 | { 36 | "login": "LadySolveig", 37 | "name": "Martina Scholz", 38 | "avatar_url": "https://avatars.githubusercontent.com/u/64533137?v=4", 39 | "profile": "https://github.com/LadySolveig", 40 | "contributions": [ 41 | "code" 42 | ] 43 | }, 44 | { 45 | "login": "escopecz", 46 | "name": "John Linhart", 47 | "avatar_url": "https://avatars.githubusercontent.com/u/1235442?v=4", 48 | "profile": "http://johnlinhart.com", 49 | "contributions": [ 50 | "review" 51 | ] 52 | }, 53 | { 54 | "login": "Rocksheep", 55 | "name": "Marinus van Velzen", 56 | "avatar_url": "https://avatars.githubusercontent.com/u/1311371?v=4", 57 | "profile": "https://github.com/Rocksheep", 58 | "contributions": [ 59 | "code" 60 | ] 61 | }, 62 | { 63 | "login": "PierreAmmeloot", 64 | "name": "Pierre Ammeloot", 65 | "avatar_url": "https://avatars.githubusercontent.com/u/4603318?v=4", 66 | "profile": "https://pierre.ammeloot.fr", 67 | "contributions": [ 68 | "userTesting" 69 | ] 70 | }, 71 | { 72 | "login": "matbcvo", 73 | "name": "Martin Vooremäe", 74 | "avatar_url": "https://avatars.githubusercontent.com/u/1006437?v=4", 75 | "profile": "https://matbcvo.github.io", 76 | "contributions": [ 77 | "code", 78 | "test" 79 | ] 80 | } 81 | ], 82 | "contributorsPerLine": 7, 83 | "projectName": "api-library", 84 | "projectOwner": "mautic", 85 | "repoType": "github", 86 | "repoHost": "https://github.com", 87 | "skipCi": true, 88 | "commitConvention": "angular", 89 | "commitType": "docs" 90 | } 91 | -------------------------------------------------------------------------------- /.ddev/config.yaml: -------------------------------------------------------------------------------- 1 | name: api-library 2 | type: php 3 | docroot: "" 4 | php_version: "8.0" 5 | webserver_type: apache-fpm 6 | router_http_port: "80" 7 | router_https_port: "443" 8 | xdebug_enabled: false 9 | additional_hostnames: [] 10 | additional_fqdns: [] 11 | mariadb_version: "10.3" 12 | mysql_version: "" 13 | provider: default 14 | use_dns_when_possible: true 15 | composer_version: "2" 16 | webimage_extra_packages: [php8.0-imap] 17 | hooks: 18 | post-start: 19 | - exec: "./.ddev/mautic-setup.sh" 20 | -------------------------------------------------------------------------------- /.ddev/local.config.php.dist: -------------------------------------------------------------------------------- 1 | = 2.4 14 | | 15 | | Pass this as the second parameter to specify which to use. 16 | | 17 | | If commented out, will default OAuth. see MauticApiTestCase::getAuth() 18 | | 19 | */ 20 | 'AuthMethod' => 'BasicAuth', 21 | 22 | /* 23 | |-------------------------------------------------------------------------- 24 | | Basic Authentication credentials 25 | |-------------------------------------------------------------------------- 26 | | 27 | | Basic Auth uses a simple username password method. 28 | | 29 | | It's important to note, that this is not recommended unless you are 30 | | using HTTPS as your username and password are exposed. 31 | | 32 | | BOTH the username and password are required. It's recommended that 33 | | you add a specific user and ensure a long passPhrase! 34 | | 35 | */ 36 | 'userName' => 'admin', 37 | 'password' => 'mautic', 38 | 39 | /* 40 | |-------------------------------------------------------------------------- 41 | | OAuth credentials 42 | |-------------------------------------------------------------------------- 43 | | 44 | | The API supports both OAuth1a and OAuth2. 45 | | 46 | | I've found there is more control if you specify which version you want 47 | | to use. 48 | | 49 | | Required mainly for OAuth 1a testing; 'OAuth1a' or 'OAuth2' 50 | | 51 | */ 52 | 'version' => 'OAuth1a', 53 | 54 | // Required for OAuth1a and OAuth2 55 | 'baseUrl' => 'http://localhost/mautic/index.php', 56 | 57 | // Required for All tests 58 | 'apiUrl' => 'http://localhost/mautic/index.php/api/', 59 | // Required for EmailsTest 60 | 'testEmail' => 'notexisting@email.com', 61 | 62 | // Required for both OAuth 1a and 2 testing 63 | 'accessToken' => '', 64 | 65 | // Required only for OAuth 1a testing 66 | 'accessTokenSecret' => '', 67 | 'requestTokenUrl' => '', // Set with any string to force the library to use the OAuth1.a spec; leave blank to use OAuth2 68 | 'clientKey' => '', 69 | 'clientSecret' => '', 70 | 'callback' => '', 71 | 72 | // Required only for OAuth 2 testing. 73 | 'refreshToken' => '', 74 | ]; 75 | -------------------------------------------------------------------------------- /.ddev/mautic-local.php.dist: -------------------------------------------------------------------------------- 1 | true, 7 | 'api_enable_basic_auth' => true, 8 | 'db_driver' => 'pdo_mysql', 9 | 'db_host' => 'db', 10 | 'db_table_prefix' => null, 11 | 'db_port' => 3306, 12 | 'db_name' => 'db', 13 | 'db_user' => 'db', 14 | 'db_password' => 'db', 15 | 'admin_email' => 'mautic@ddev.local', 16 | 'admin_password' => 'mautic', 17 | 'mailer_from_name' => 'DDEV', 18 | 'mailer_from_email' => 'mautic@ddev.local', 19 | 'mailer_transport' => 'smtp', 20 | 'mailer_host' => 'localhost', 21 | 'mailer_port' => '1025', 22 | ]; 23 | -------------------------------------------------------------------------------- /.ddev/mautic-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | setup_mautic() { 4 | cp ./.ddev/local.config.php.dist ./tests/local.config.php 5 | 6 | printf "Cloning the \"features\" branch from mautic/mautic...\n" 7 | git clone -b features --single-branch --depth 1 https://github.com/mautic/mautic.git mautic 8 | cd mautic 9 | 10 | composer install --prefer-dist --no-progress 11 | cp ../.ddev/mautic-local.php.dist ./app/config/local.php 12 | 13 | printf "Installing Mautic...\n" 14 | php bin/console mautic:install --force http://localhost/mautic 15 | php bin/console cache:warmup --no-interaction --env=dev 16 | 17 | printf "Enabling Twilio plugin for tests...\n" 18 | mysql -udb -pdb -P3306 -hdb -e "USE db; INSERT INTO plugin_integration_settings (id, plugin_id, name, is_published, supported_features, api_keys, feature_settings) VALUES (2, NULL, 'Twilio', 1, 'a:0:{}', 'a:2:{s:8:\"username\";s:169:\"bzFmNlIydWRSZXlIN2lQVkdpanJ4aTQ2NUh6RVdDbHlLRVhsWGZ4b0kyZVNxLzYrQ1J6V1RvMnlhVEp0c245TEp6eStQekx5ZVhLWjB1YVdoR3RnR2dHQ3k1emVVdGt5NzZKUmtjUnJ3c1E9|L8tbZRIYhwatT7Mq+HAdYA==\";s:8:\"password\";s:169:\"T2d2cFpXQWE5YVZnNFFianJSYURRYUtGRHBNZGZjM1VETXg2Wm5Va3NheW43MjVWUlJhTVlCL2pYMDBpbElONStiVVBNbEM3M3BaeGJMNkFKNUFEN1pTNldSRjc4bUM4SDh1SE9OY1k5MTg9|TeuSvfx4XSUOvp0O7T49Cg==\";}', 'a:4:{s:20:\"sending_phone_number\";N;s:22:\"disable_trackable_urls\";i:0;s:16:\"frequency_number\";N;s:14:\"frequency_time\";N;}');" 19 | php bin/console mautic:plugins:reload 20 | 21 | tput setaf 2 22 | printf "All done! Run \"ddev exec composer test\" to run PHPUnit tests.\n" 23 | printf "If you want to open the Mautic instance, go to https://api-library.ddev.site/mautic in your browser.\n" 24 | tput sgr0 25 | } 26 | 27 | # Check if the user has indicated their preference for the Mautic installation 28 | # already (DDEV-managed or self-managed) 29 | if ! test -f ./.ddev/mautic-preference 30 | then 31 | printf "Installing the API library Composer dependencies...\n" 32 | composer install 33 | tput setab 3 34 | tput setaf 0 35 | printf "Do you want us to set up a Mautic instance for you to test against?\n" 36 | printf "If you answer \"no\", you will have to set up a Mautic instance yourself." 37 | tput sgr0 38 | printf "\nAnswer [yes/no]: " 39 | read MAUTIC_PREF 40 | 41 | if [[ $MAUTIC_PREF == "yes" ]] 42 | then 43 | printf "Okay, setting up a Mautic instance...\n" 44 | echo "ddev-managed" > ./.ddev/mautic-preference 45 | setup_mautic 46 | else 47 | printf "Okay, you'll have to set up a Mautic instance yourself. " 48 | printf "Copy /tests/local.config.php.dist to /tests/local.config.php and add your Mautic instance settings.\n" 49 | echo "unmanaged" > ./.ddev/mautic-preference 50 | fi 51 | else 52 | # Ensure our mautic/mautic clone is up-to-date 53 | echo "Updating the cloned Mautic instance..." 54 | cd mautic 55 | git pull 56 | # Need to downgrade to Composer v1 until Mautic 4 is out 57 | composer self-update --1 58 | composer install --prefer-dist --no-progress 59 | tput setaf 2 60 | printf "Run \"ddev exec composer test\" to run PHPUnit tests.\n" 61 | printf "If you want to open the Mautic instance, go to https://api-library.ddev.site/mautic in your browser.\n" 62 | tput sgr0 63 | fi 64 | -------------------------------------------------------------------------------- /.github/ci-files/.env: -------------------------------------------------------------------------------- 1 | # In all environments, the following files are loaded if they exist, 2 | # the latter taking precedence over the former: 3 | # 4 | # * .env contains default values for the environment variables needed by the app 5 | # * .env.local uncommitted file with local overrides 6 | # * .env.$APP_ENV committed environment-specific defaults 7 | # * .env.$APP_ENV.local uncommitted environment-specific overrides 8 | # 9 | # Real environment variables win over .env files. 10 | # 11 | # DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES. 12 | # 13 | # Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2). 14 | # https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration 15 | APP_ENV=test 16 | APP_DEBUG=0 17 | 18 | DB_HOST=127.0.0.1 19 | DB_NAME=mautictest 20 | DB_USER=root 21 | DB_PORT=3306 22 | DB_PASSWD= 23 | MAUTIC_DB_PREFIX= 24 | MAUTIC_TABLE_PREFIX= 25 | MAUTIC_ENV=test 26 | MAUTIC_ADMIN_USERNAME=admin 27 | MAUTIC_ADMIN_PASSWORD=mautic 28 | MAUTIC_DB_DRIVER=pdo_mysql 29 | -------------------------------------------------------------------------------- /.github/ci-files/local.config.php: -------------------------------------------------------------------------------- 1 | = 2.4 14 | | 15 | | Pass this as the second parameter to specify which to use. 16 | | 17 | | If commented out, will default OAuth. see MauticApiTestCase::getAuth() 18 | | 19 | */ 20 | 'AuthMethod' => 'BasicAuth', 21 | 22 | /* 23 | |-------------------------------------------------------------------------- 24 | | Basic Authentication credentials 25 | |-------------------------------------------------------------------------- 26 | | 27 | | Basic Auth uses a simple username password method. 28 | | 29 | | It's important to note, that this is not recommended unless you are 30 | | using HTTPS as your username and password are exposed. 31 | | 32 | | BOTH the username and password are required. It's recommended that 33 | | you add a specific user and ensure a long passPhrase! 34 | | 35 | */ 36 | 'userName' => 'admin', 37 | 'password' => 'Maut1cR0cks!', 38 | 39 | /* 40 | |-------------------------------------------------------------------------- 41 | | OAuth credentials 42 | |-------------------------------------------------------------------------- 43 | | 44 | | The API supports both OAuth1a and OAuth2. 45 | | 46 | | I've found there is more control if you specify which version you want 47 | | to use. 48 | | 49 | | Required mainly for OAuth 1a testing; 'OAuth1a' or 'OAuth2' 50 | | 51 | */ 52 | 'version' => 'OAuth1a', 53 | 54 | // Required for OAuth1a and OAuth2 55 | 'baseUrl' => 'http://localhost/index.php', 56 | 57 | // Required for All tests 58 | 'apiUrl' => 'http://localhost/index.php/api/', 59 | // Required for EmailsTest 60 | 'testEmail' => 'notexisting@email.com', 61 | 62 | // Required for both OAuth 1a and 2 testing 63 | 'accessToken' => '', 64 | 65 | // Required only for OAuth 1a testing 66 | 'accessTokenSecret' => '', 67 | 'requestTokenUrl' => '', // Set with any string to force the library to use the OAuth1.a spec; leave blank to use OAuth2 68 | 'clientKey' => '', 69 | 'clientSecret' => '', 70 | 'callback' => '', 71 | 72 | // Required only for OAuth 2 testing. 73 | 'refreshToken' => '', 74 | ]; 75 | -------------------------------------------------------------------------------- /.github/ci-files/local_4.php: -------------------------------------------------------------------------------- 1 | true, 7 | 'api_enable_basic_auth' => true, 8 | 'db_driver' => 'pdo_mysql', 9 | 'db_host' => '127.0.0.1', 10 | 'db_table_prefix' => null, 11 | 'db_port' => getenv('DB_PORT'), 12 | 'db_name' => 'mautictest', 13 | 'db_user' => 'root', 14 | 'db_password' => '', 15 | 'admin_email' => 'github-actions@mautic.org', 16 | 'admin_password' => 'mautic', 17 | 'mailer_transport' => 'smtp', 18 | 'mailer_host' => 'localhost', 19 | 'mailer_port' => '1025', 20 | ]; 21 | -------------------------------------------------------------------------------- /.github/ci-files/local_5.php: -------------------------------------------------------------------------------- 1 | true, 7 | 'api_enable_basic_auth' => true, 8 | 'db_driver' => 'pdo_mysql', 9 | 'db_host' => '127.0.0.1', 10 | 'db_table_prefix' => null, 11 | 'db_port' => getenv('DB_PORT'), 12 | 'db_name' => 'mautictest', 13 | 'db_user' => 'root', 14 | 'db_password' => '', 15 | 'admin_email' => 'github-actions@mautic.org', 16 | 'admin_password' => 'Maut1cR0cks!', 17 | 'mailer_from_name' => 'GitHub Actions', 18 | 'mailer_from_email' => 'github-actions@mautic.org', 19 | 'mailer_transport' => 'smtp', 20 | 'mailer_dsn' => 'smtp://localhost:1025', 21 | ]; 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /.buildpath 3 | /.project 4 | /.settings 5 | /.phpintel 6 | /phpunit.xml 7 | /tests/local.config.php 8 | /tests/local.config.php_* 9 | /tests/local.tokens.php 10 | /tests/phpunit.phar 11 | /composer.lock 12 | /vendor 13 | .DS_Store 14 | /.php-cs-fixer.cache 15 | .phpunit.result.cache 16 | /.ddev/mautic-preference 17 | /mautic 18 | -------------------------------------------------------------------------------- /.php-cs-fixer.php: -------------------------------------------------------------------------------- 1 | in(__DIR__.'/lib') 5 | ->in(__DIR__.'/tests'); 6 | 7 | $config = new PhpCsFixer\Config(); 8 | 9 | return $config 10 | ->setRules([ 11 | '@Symfony' => true, 12 | 'binary_operator_spaces' => [ 13 | 'operators' => [ 14 | '=' => 'align', 15 | '=>' => 'align', 16 | ], 17 | ], 18 | 'ordered_imports' => true, 19 | 'array_syntax' => [ 20 | 'syntax' => 'short' 21 | ], 22 | 'no_unused_imports' => false, 23 | ]) 24 | ->setFinder($finder); 25 | -------------------------------------------------------------------------------- /.well-known/funding-manifest-urls: -------------------------------------------------------------------------------- 1 | https://github.com/mautic/mautic/blob/5.x/funding.json 2 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /UPGRADE-4.0.md: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | * PHP 8.0 is the now the minimum 3 | * We are decoupled from any HTTP messaging client with the help of [PSR-18 HTTP Client](https://www.php-fig.org/psr/psr-18/). This requires an extra package providing [psr/http-client-implementation](https://packagist.org/providers/psr/http-client-implementation). To use Guzzle 7, for example, simply require `guzzlehttp/guzzle` in your project. If you do not have an HTTP client installed, `php-http/discovery` will install one if you allow the Composer plugin. 4 | 5 | # Api 6 | * \Mautic\Api\Api::getLogger now returns void as the LoggerAwareInterface dictates 7 | * \Mautic\Api\Api::getResponseInfo and \Mautic\Response::getInfo have been removed, all Auth classes now have a getResponse method to get the PSR-7 response message 8 | 9 | # HTTP 10 | ## Timeout 11 | The setCurlTimeout-method (`$auth->setCurlTimeout(10);`) has been removed. If you like to set the timeout, you should configure your HTTP client to do so. For example, with Guzzle 7, you can do this: 12 | 13 | ```php 14 | $httpClient = new \GuzzleHttp\Client([ 15 | 'timeout' => 10, 16 | ]); 17 | $settings = [ 18 | // ... 19 | ]; 20 | 21 | $initAuth = new \Mautic\Auth\ApiAuth($httpClient); 22 | $auth = $initAuth->newAuth($settings); 23 | ``` 24 | -------------------------------------------------------------------------------- /apitester/endpoints/Assets.json: -------------------------------------------------------------------------------- 1 | [ 2 | "assets", 3 | "assets/{id}", 4 | "assets/new", 5 | "assets/batch/new", 6 | "assets/batch/edit", 7 | "assets/batch/delete", 8 | "assets/{id}/edit", 9 | "assets/{id}/delete" 10 | ] -------------------------------------------------------------------------------- /apitester/endpoints/Campaigns.json: -------------------------------------------------------------------------------- 1 | [ 2 | "campaigns", 3 | "campaigns/{id}", 4 | "campaigns/new", 5 | "campaigns/batch/new", 6 | "campaigns/batch/edit", 7 | "campaigns/batch/delete", 8 | "campaigns/{id}/edit", 9 | "campaigns/{id}/delete", 10 | "campaigns/{id}/contact/{contactId}/add", 11 | "campaigns/{id}/contact/{contactId}/remove", 12 | "campaigns/events", 13 | "campaigns/events/{id}", 14 | "campaigns/events/{eventId}/contact/{contactId}/edit", 15 | "campaigns/events/batch/edit" 16 | ] -------------------------------------------------------------------------------- /apitester/endpoints/Companies.json: -------------------------------------------------------------------------------- 1 | [ 2 | "companies", 3 | "companies/{id}", 4 | "companies/new", 5 | "companies/batch/new", 6 | "companies/batch/edit", 7 | "companies/batch/delete", 8 | "companies/{id}/edit", 9 | "companies/{id}/delete", 10 | "companies/{id}/contact/{contactId}/add", 11 | "companies/{id}/contact/{contactId}/remove" 12 | ] -------------------------------------------------------------------------------- /apitester/endpoints/Contacts.json: -------------------------------------------------------------------------------- 1 | [ 2 | "contacts", 3 | "contacts/{id}", 4 | "contacts/list/fields", 5 | "contacts/list/owners", 6 | "contacts/new", 7 | "contacts/batch/new", 8 | "contacts/batch/edit", 9 | "contacts/batch/delete", 10 | "contacts/{id}/edit", 11 | "contacts/{id}/delete", 12 | "contacts/{id}/notes", 13 | "contacts/{id}/segments", 14 | "contacts/{id}/campaigns", 15 | "contacts/{id}/points/plus/{points}", 16 | "contacts/{id}/points/minus/{points}", 17 | "contacts/{id}/points/times/{points}", 18 | "contacts/{id}/points/divide/{points}" 19 | ] -------------------------------------------------------------------------------- /apitester/endpoints/Data.json: -------------------------------------------------------------------------------- 1 | [ 2 | "data", 3 | "data/{type}", 4 | ] 5 | -------------------------------------------------------------------------------- /apitester/endpoints/Emails.json: -------------------------------------------------------------------------------- 1 | [ 2 | "emails", 3 | "emails/{id}", 4 | "emails/new", 5 | "emails/batch/new", 6 | "emails/batch/edit", 7 | "emails/batch/delete", 8 | "emails/{id}/edit", 9 | "emails/{id}/delete", 10 | "emails/{id}/send", 11 | "emails/{id}/contact/{contactId}/send" 12 | ] -------------------------------------------------------------------------------- /apitester/endpoints/Focus.json: -------------------------------------------------------------------------------- 1 | [ 2 | "focus", 3 | "focus/{id}", 4 | "focus/new", 5 | "focus/batch/new", 6 | "focus/batch/edit", 7 | "focus/batch/delete", 8 | "focus/{id}/edit", 9 | "focus/{id}/delete" 10 | ] -------------------------------------------------------------------------------- /apitester/endpoints/Forms.json: -------------------------------------------------------------------------------- 1 | [ 2 | "forms", 3 | "forms/{id}", 4 | "forms/new", 5 | "forms/batch/new", 6 | "forms/batch/edit", 7 | "forms/batch/delete", 8 | "forms/{id}/edit", 9 | "forms/{id}/delete" 10 | ] -------------------------------------------------------------------------------- /apitester/endpoints/Notifications.json: -------------------------------------------------------------------------------- 1 | [ 2 | "smses", 3 | "smses/{id}", 4 | "smses/new", 5 | "smses/batch/new", 6 | "smses/batch/edit", 7 | "smses/batch/delete", 8 | "smses/{id}/edit", 9 | "smses/{id}/delete" 10 | ] -------------------------------------------------------------------------------- /apitester/endpoints/Pages.json: -------------------------------------------------------------------------------- 1 | [ 2 | "pages", 3 | "pages/{id}", 4 | "pages/new", 5 | "pages/batch/new", 6 | "pages/batch/edit", 7 | "pages/batch/delete", 8 | "pages/{id}/edit", 9 | "pages/{id}/delete" 10 | ] -------------------------------------------------------------------------------- /apitester/endpoints/Points.json: -------------------------------------------------------------------------------- 1 | [ 2 | "points", 3 | "points/{id}", 4 | "points/new", 5 | "points/batch/new", 6 | "points/batch/edit", 7 | "points/batch/delete", 8 | "points/{id}/edit", 9 | "points/{id}/delete", 10 | "points/triggers", 11 | "points/triggers/{id}", 12 | "points/triggers/new", 13 | "points/triggers/batch/new", 14 | "points/triggers/batch/edit", 15 | "points/triggers/batch/delete", 16 | "points/triggers/{id}/edit", 17 | "points/triggers/{id}/delete" 18 | ] 19 | -------------------------------------------------------------------------------- /apitester/endpoints/Reports.json: -------------------------------------------------------------------------------- 1 | [ 2 | "reports", 3 | "reports/{id}" 4 | ] 5 | -------------------------------------------------------------------------------- /apitester/endpoints/Segments.json: -------------------------------------------------------------------------------- 1 | [ 2 | "segments", 3 | "segments/new", 4 | "segments/batch/new", 5 | "segments/batch/edit", 6 | "segments/batch/delete", 7 | "segments/{id}/edit", 8 | "segments/{id}/delete", 9 | "segments/{id}/contact/{contactId}/add", 10 | "segments/{id}/contact/{contactId}/remove" 11 | ] -------------------------------------------------------------------------------- /apitester/endpoints/Smses.json: -------------------------------------------------------------------------------- 1 | [ 2 | "notifications", 3 | "notifications/{id}", 4 | "notifications/new", 5 | "notifications/batch/new", 6 | "notifications/batch/edit", 7 | "notifications/batch/delete", 8 | "notifications/{id}/edit", 9 | "notifications/{id}/delete" 10 | ] -------------------------------------------------------------------------------- /apitester/endpoints/Stages.json: -------------------------------------------------------------------------------- 1 | [ 2 | "stages", 3 | "stages/{id}", 4 | "stages/new", 5 | "stages/batch/new", 6 | "stages/batch/edit", 7 | "stages/batch/delete", 8 | "stages/{id}/edit", 9 | "stages/{id}/delete", 10 | ] -------------------------------------------------------------------------------- /apitester/endpoints/Users.json: -------------------------------------------------------------------------------- 1 | [ 2 | "roles", 3 | "roles/{id}", 4 | "roles/new", 5 | "roles/batch/new", 6 | "roles/batch/edit", 7 | "roles/batch/delete", 8 | "roles/{id}/edit", 9 | "roles/{id}/delete", 10 | "users", 11 | "users/{id}", 12 | "users/new", 13 | "users/batch/new", 14 | "users/batch/edit", 15 | "users/batch/delete", 16 | "users/{id}/edit", 17 | "users/{id}/delete", 18 | "users/list/roles", 19 | "users/self", 20 | "users/{id}/permissioncheck" 21 | ] -------------------------------------------------------------------------------- /apitester/includes/bootstrap/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mautic/api-library/d0f6603a768f139cb17ed4655b3cabf9a1cd4b83/apitester/includes/bootstrap/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /apitester/includes/bootstrap/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mautic/api-library/d0f6603a768f139cb17ed4655b3cabf9a1cd4b83/apitester/includes/bootstrap/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /apitester/includes/bootstrap/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mautic/api-library/d0f6603a768f139cb17ed4655b3cabf9a1cd4b83/apitester/includes/bootstrap/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /apitester/includes/style.css: -------------------------------------------------------------------------------- 1 | .custom { 2 | -webkit-border-radius: 0px !important; 3 | -moz-border-radius: 0px !important; 4 | border-radius: 0px !important; 5 | } 6 | .custom-right { 7 | -webkit-border-radius: 4px 0px 0px 4px !important; 8 | -moz-border-radius: 4px 0px 0px 4px !important; 9 | border-radius: 4px 0px 0px 4px !important; 10 | width: 100%; 11 | } 12 | .parameter-row { 13 | margin-bottom: 3px; 14 | } 15 | .twitter-typeahead .tt-query, 16 | .twitter-typeahead .tt-hint { 17 | margin-bottom: 0; 18 | } 19 | .tt-hint { 20 | display: block; 21 | width: 100%; 22 | height: 38px; 23 | padding: 8px 12px 10px 12px; 24 | font-size: 14px; 25 | line-height: 1.428571429; 26 | color: #999; 27 | vertical-align: middle; 28 | background-color: #ffffff; 29 | border: 1px solid #cccccc; 30 | border-radius: 4px; 31 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); 32 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); 33 | -webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; 34 | transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; 35 | } 36 | .tt-dropdown-menu { 37 | min-width: 160px; 38 | margin-top: 2px; 39 | padding: 5px 0; 40 | background-color: #ffffff; 41 | border: 1px solid #cccccc; 42 | border: 1px solid rgba(0, 0, 0, 0.15); 43 | border-radius: 4px; 44 | -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); 45 | box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); 46 | background-clip: padding-box; 47 | 48 | } 49 | .tt-suggestion { 50 | display: block; 51 | padding: 3px 20px; 52 | } 53 | .tt-suggestion.tt-is-under-cursor { 54 | color: #fff; 55 | background-color: #428bca; 56 | } 57 | .tt-suggestion.tt-is-under-cursor a { 58 | color: #fff; 59 | } 60 | .tt-suggestion p { 61 | margin: 0; 62 | } 63 | .twitter-typeahead { width: 100%; } 64 | 65 | .endpoint-name { 66 | margin: 0 20px 5px 20px; 67 | padding: 3px 0; 68 | border-bottom: 1px solid #ccc; 69 | } 70 | 71 | #scrollable-dropdown-menu .tt-dropdown-menu { 72 | max-height: 150px; 73 | overflow-y: auto; 74 | } -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mautic/api-library", 3 | "license": "MIT", 4 | "type": "library", 5 | "description": "Mautic API Connector Library", 6 | "autoload": { 7 | "psr-4": { 8 | "Mautic\\": "lib/" 9 | } 10 | }, 11 | "autoload-dev": { 12 | "psr-4": { 13 | "Mautic\\Tests\\": "tests/" 14 | } 15 | }, 16 | "require": { 17 | "php": ">=8.0", 18 | "ext-json": "*", 19 | "psr/log": "~1.0 || ~2.0 || ~3.0", 20 | "psr/http-client": "^1.0", 21 | "psr/http-client-implementation": "^1.0", 22 | "guzzlehttp/psr7": "^2.4", 23 | "php-http/discovery": "^1.15" 24 | }, 25 | "require-dev": { 26 | "kint-php/kint": "^4.1", 27 | "friendsofphp/php-cs-fixer": "^3.73", 28 | "phpunit/phpunit": "~9.5.0", 29 | "guzzlehttp/guzzle": "^7.5", 30 | "phpstan/phpstan": "^1.11" 31 | }, 32 | "suggest": { 33 | "guzzlehttp/guzzle": "A popular HTTP client that implements psr/http-client-implementation." 34 | }, 35 | "scripts": { 36 | "test": "vendor/bin/phpunit", 37 | "phpstan": "vendor/bin/phpstan analyse" 38 | }, 39 | "config": { 40 | "allow-plugins": { 41 | "php-http/discovery": true 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/Api/Assets.php: -------------------------------------------------------------------------------- 1 | $search, 49 | 'start' => $start, 50 | 'limit' => $limit, 51 | 'orderBy' => $orderBy, 52 | 'orderByDir' => $orderByDir, 53 | ]; 54 | 55 | $parameters = array_filter($parameters); 56 | 57 | return $this->makeRequest($this->endpoint.'/contact/'.$contactId, $parameters); 58 | } 59 | 60 | /** 61 | * Get contact events for a single campaign. 62 | * 63 | * @param int $campaignId 64 | * @param int $contactId 65 | * @param string $search 66 | * @param int $start 67 | * @param int $limit 68 | * @param string $orderBy 69 | * @param string $orderByDir 70 | * 71 | * @return array|mixed 72 | */ 73 | public function getContactCampaignEvents($campaignId, $contactId, $search = '', $start = 0, $limit = 0, $orderBy = '', $orderByDir = 'ASC') 74 | { 75 | $parameters = [ 76 | 'search' => $search, 77 | 'start' => $start, 78 | 'limit' => $limit, 79 | 'orderBy' => $orderBy, 80 | 'orderByDir' => $orderByDir, 81 | ]; 82 | 83 | $parameters = array_filter($parameters); 84 | 85 | return $this->makeRequest('campaigns/'.$campaignId.'/events/contact/'.$contactId, $parameters); 86 | } 87 | 88 | /** 89 | * Edit or schedule a campaign event for a specific contact. 90 | * 91 | * @param int $contactId 92 | * @param int $eventId 93 | * 94 | * @return array|mixed 95 | */ 96 | public function editContactEvent($contactId, $eventId, array $parameters) 97 | { 98 | return $this->makeRequest($this->endpoint.'/'.$eventId.'/contact/'.$contactId.'/edit', $parameters, 'PUT'); 99 | } 100 | 101 | /** 102 | * Edit or schedule multiple events. 103 | * 104 | * @return array|mixed 105 | */ 106 | public function editEvents(array $parameters) 107 | { 108 | return $this->makeRequest($this->endpoint.'/batch/edit', $parameters, 'PUT'); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /lib/Api/Campaigns.php: -------------------------------------------------------------------------------- 1 | 'campaigns/$1/contact/add/$2', // 2.6.0 30 | 'campaigns/(.*?)/contact/(.*?)/remove' => 'campaigns/$1/contact/remove/$2', // 2.6.0 31 | ]; 32 | 33 | protected $searchCommands = [ 34 | 'ids', 35 | 'is:published', 36 | 'is:unpublished', 37 | 'is:mine', 38 | 'is:uncategorized', 39 | 'category', 40 | ]; 41 | 42 | /** 43 | * Add a lead to the campaign. 44 | * 45 | * @deprecated 2.0.1, use addContact instead 46 | * 47 | * @param int $id Campaign ID 48 | * @param int $leadId Lead ID 49 | * 50 | * @return array|mixed 51 | */ 52 | public function addLead($id, $leadId) 53 | { 54 | return $this->addContact($id, $leadId); 55 | } 56 | 57 | /** 58 | * Add a contact to the campaign. 59 | * 60 | * @param int $id Campaign ID 61 | * @param int $contactId Contact ID 62 | * 63 | * @return array|mixed 64 | */ 65 | public function addContact($id, $contactId) 66 | { 67 | return $this->makeRequest($this->endpoint.'/'.$id.'/contact/'.$contactId.'/add', [], 'POST'); 68 | } 69 | 70 | /** 71 | * Remove a lead from the campaign. 72 | * 73 | * @deprecated 2.0.1, use removeContact instead 74 | * 75 | * @param int $id Campaign ID 76 | * @param int $leadId Lead ID 77 | * 78 | * @return array|mixed 79 | */ 80 | public function removeLead($id, $leadId) 81 | { 82 | return $this->removeContact($id, $leadId); 83 | } 84 | 85 | /** 86 | * Remove a contact from the campaign. 87 | * 88 | * @param int $id Campaign ID 89 | * @param int $contactId Contact ID 90 | * 91 | * @return array|mixed 92 | */ 93 | public function removeContact($id, $contactId) 94 | { 95 | return $this->makeRequest($this->endpoint.'/'.$id.'/contact/'.$contactId.'/remove', [], 'POST'); 96 | } 97 | 98 | /** 99 | * Get a list of stat items. 100 | * 101 | * @param int $id Campaign ID 102 | * @param int $start 103 | * @param int $limit 104 | * 105 | * @return array|mixed 106 | */ 107 | public function getContacts($id, $start = 0, $limit = 0, array $order = [], array $where = []) 108 | { 109 | $parameters = []; 110 | $args = ['start', 'limit', 'order', 'where']; 111 | 112 | foreach ($args as $arg) { 113 | if (!empty($$arg)) { 114 | $parameters[$arg] = $$arg; 115 | } 116 | } 117 | 118 | return $this->makeRequest($this->endpoint.'/'.$id.'/contacts', $parameters); 119 | } 120 | 121 | /** 122 | * Clone an Existing campaign. 123 | * 124 | * @param int $id Campaign ID 125 | * 126 | * @return array|mixed 127 | */ 128 | public function cloneCampaign($id) 129 | { 130 | return $this->makeRequest($this->endpoint.'/clone/'.$id, [], 'POST'); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /lib/Api/Categories.php: -------------------------------------------------------------------------------- 1 | 'companies/$1/contact/add/$2', // 2.6.0 30 | 'companies/(.*?)/contact/(.*?)/remove' => 'companies/$1/contact/remove/$2', // 2.6.0 31 | ]; 32 | 33 | protected $searchCommands = [ 34 | 'ids', 35 | 'is:mine', 36 | ]; 37 | 38 | /** 39 | * Add a contact to the company. 40 | * 41 | * @param int $id Company ID 42 | * @param int $contactId Contact ID 43 | * 44 | * @return array|mixed 45 | */ 46 | public function addContact($id, $contactId) 47 | { 48 | return $this->makeRequest($this->endpoint.'/'.$id.'/contact/'.$contactId.'/add', [], 'POST'); 49 | } 50 | 51 | /** 52 | * Remove a contact from the company. 53 | * 54 | * @param int $id Company ID 55 | * @param int $contactId Contact ID 56 | * 57 | * @return array|mixed 58 | */ 59 | public function removeContact($id, $contactId) 60 | { 61 | return $this->makeRequest($this->endpoint.'/'.$id.'/contact/'.$contactId.'/remove', [], 'POST'); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/Api/CompanyFields.php: -------------------------------------------------------------------------------- 1 | makeRequest("{$this->endpoint}/$id", $options); 36 | } 37 | 38 | public function getPublishedList($search = '', $start = 0, $limit = 0, $orderBy = '', $orderByDir = 'ASC') 39 | { 40 | return $this->actionNotSupported(__FUNCTION__); 41 | } 42 | 43 | public function create(array $parameters) 44 | { 45 | return $this->actionNotSupported(__FUNCTION__); 46 | } 47 | 48 | public function edit($id, array $parameters, $createIfNotExists = false) 49 | { 50 | return $this->actionNotSupported(__FUNCTION__); 51 | } 52 | 53 | public function delete($id) 54 | { 55 | return $this->actionNotSupported(__FUNCTION__); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/Api/Devices.php: -------------------------------------------------------------------------------- 1 | 'emails/$1/send/contact/$2', // 2.6.0 30 | ]; 31 | 32 | protected $searchCommands = [ 33 | 'ids', 34 | 'is:published', 35 | 'is:unpublished', 36 | 'is:mine', 37 | 'is:uncategorized', 38 | 'category', 39 | 'lang', 40 | ]; 41 | 42 | /** 43 | * Send email to the assigned lists. 44 | * 45 | * @param int $id 46 | * 47 | * @return array|mixed 48 | */ 49 | public function send($id) 50 | { 51 | return $this->makeRequest($this->endpoint.'/'.$id.'/send', [], 'POST'); 52 | } 53 | 54 | /** 55 | * Send email to a specific contact. 56 | * 57 | * @param int $id 58 | * @param int $contactId 59 | * 60 | * @return array|mixed 61 | */ 62 | public function sendToContact($id, $contactId, $parameters = []) 63 | { 64 | return $this->makeRequest($this->endpoint.'/'.$id.'/contact/'.$contactId.'/send', $parameters, 'POST'); 65 | } 66 | 67 | /** 68 | * Send email to a specific lead. 69 | * 70 | * @deprecated use sendToContact instead 71 | * 72 | * @param int $id 73 | * @param int $leadId 74 | * 75 | * @return array|mixed 76 | */ 77 | public function sendToLead($id, $leadId) 78 | { 79 | return $this->sendToContact($id, $leadId); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /lib/Api/Files.php: -------------------------------------------------------------------------------- 1 | endpoint = 'files/'.$folder; 34 | } 35 | 36 | public function edit($id, array $parameters, $createIfNotExists = false) 37 | { 38 | return $this->actionNotSupported('edit'); 39 | } 40 | 41 | /** 42 | * @return array|mixed 43 | */ 44 | public function create(array $parameters) 45 | { 46 | if (!isset($parameters['file'])) { 47 | throw new \InvalidArgumentException('file must be set in parameters'); 48 | } 49 | 50 | return parent::create($parameters); 51 | } 52 | 53 | public function createBatch(array $parameters) 54 | { 55 | return $this->actionNotSupported('createBatch'); 56 | } 57 | 58 | public function editBatch(array $parameters, $createIfNotExists = false) 59 | { 60 | return $this->actionNotSupported('editBatch'); 61 | } 62 | 63 | public function deleteBatch(array $ids) 64 | { 65 | return $this->actionNotSupported('deleteBatch'); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/Api/Focus.php: -------------------------------------------------------------------------------- 1 | makeRequest($this->endpoint.'/'.$formId.'/fields/delete', ['fields' => $fieldIds], 'DELETE'); 46 | } 47 | 48 | /** 49 | * Remove actions from a form. 50 | * 51 | * @param int $formId 52 | * 53 | * @return array|mixed 54 | */ 55 | public function deleteActions($formId, array $actionIds) 56 | { 57 | return $this->makeRequest($this->endpoint.'/'.$formId.'/actions/delete', ['actions' => $actionIds], 'DELETE'); 58 | } 59 | 60 | /** 61 | * Get a single submission. 62 | * 63 | * @param int $formId 64 | * @param int $submissionId 65 | * 66 | * @return array|mixed 67 | */ 68 | public function getSubmission($formId, $submissionId) 69 | { 70 | return $this->makeRequest("{$this->endpoint}/$formId/submissions/$submissionId"); 71 | } 72 | 73 | /** 74 | * Get a list of form submissions. 75 | * 76 | * @param int $formId 77 | * @param string $search 78 | * @param int $start 79 | * @param int $limit 80 | * @param string $orderBy 81 | * @param string $orderByDir 82 | * @param bool $publishedOnly 83 | * @param bool $minimal 84 | * 85 | * @return array|mixed 86 | */ 87 | public function getSubmissions($formId, $search = '', $start = 0, $limit = 0, $orderBy = '', $orderByDir = 'ASC', $publishedOnly = false, $minimal = false) 88 | { 89 | $parameters = [ 90 | 'search' => $search, 91 | 'start' => $start, 92 | 'limit' => $limit, 93 | 'orderBy' => $orderBy, 94 | 'orderByDir' => $orderByDir, 95 | 'publishedOnly' => $publishedOnly, 96 | 'minimal' => $minimal, 97 | ]; 98 | 99 | $parameters = array_filter($parameters); 100 | 101 | return $this->makeRequest("{$this->endpoint}/$formId/submissions", $parameters); 102 | } 103 | 104 | /** 105 | * Get a list of form submissions for specific form and contact. 106 | * 107 | * @param int $formId 108 | * @param int $contactId 109 | * @param string $search 110 | * @param int $start 111 | * @param int $limit 112 | * @param string $orderBy 113 | * @param string $orderByDir 114 | * @param bool $publishedOnly 115 | * @param bool $minimal 116 | * 117 | * @return array|mixed 118 | */ 119 | public function getSubmissionsForContact($formId, $contactId, $search = '', $start = 0, $limit = 0, $orderBy = '', $orderByDir = 'ASC', $publishedOnly = false, $minimal = false) 120 | { 121 | $parameters = [ 122 | 'search' => $search, 123 | 'start' => $start, 124 | 'limit' => $limit, 125 | 'orderBy' => $orderBy, 126 | 'orderByDir' => $orderByDir, 127 | 'publishedOnly' => $publishedOnly, 128 | 'minimal' => $minimal, 129 | ]; 130 | 131 | $parameters = array_filter($parameters); 132 | 133 | return $this->makeRequest("{$this->endpoint}/$formId/submissions/contact/$contactId", $parameters); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /lib/Api/Messages.php: -------------------------------------------------------------------------------- 1 | makeRequest($this->endpoint.'/'.$triggerId.'/events/delete', ['events' => $eventIds], 'DELETE'); 39 | } 40 | 41 | /** 42 | * Get list of available event types. 43 | * 44 | * @return array|mixed 45 | */ 46 | public function getEventTypes() 47 | { 48 | return $this->makeRequest($this->endpoint.'/events/types'); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/Api/Points.php: -------------------------------------------------------------------------------- 1 | makeRequest($this->endpoint.'/actions/types'); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/Api/Reports.php: -------------------------------------------------------------------------------- 1 | format('c'); 63 | } 64 | 65 | if ($dateTo) { 66 | $options['dateTo'] = $dateTo->format('c'); 67 | } 68 | 69 | return $this->makeRequest("{$this->endpoint}/$id", $options); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/Api/Roles.php: -------------------------------------------------------------------------------- 1 | 'segments/$1/contact/add/$2', // 2.6.0 30 | 'segments/(.*?)/contact/(.*?)/remove' => 'segments/$1/contact/remove/$2', // 2.6.0 31 | ]; 32 | 33 | /** 34 | * Add a contact to the segment. 35 | * 36 | * @param int $segmentId Segment ID 37 | * @param int $contactId Contact ID 38 | * 39 | * @return array|mixed 40 | */ 41 | public function addContact($segmentId, $contactId) 42 | { 43 | return $this->makeRequest($this->endpoint.'/'.$segmentId.'/contact/'.$contactId.'/add', [], 'POST'); 44 | } 45 | 46 | /** 47 | * Add a contact list of ids to the segment 48 | * list of contact must be added in ids[] query parameter. 49 | * 50 | * @param int $segmentId Segment ID 51 | * @param array $contactIds 52 | * 53 | * @return array|mixed 54 | */ 55 | public function addContacts($segmentId, $contactIds) 56 | { 57 | return $this->makeRequest($this->endpoint.'/'.$segmentId.'/contacts/add', $contactIds, 'POST'); 58 | } 59 | 60 | /** 61 | * Add a lead to the segment. 62 | * 63 | * @deprecated 2.0.1, use addContact() instead 64 | * 65 | * @param int $id Segment ID 66 | * @param int $leadId Lead ID 67 | * 68 | * @return array|mixed 69 | */ 70 | public function addLead($id, $leadId) 71 | { 72 | return $this->addContact($id, $leadId); 73 | } 74 | 75 | /** 76 | * Remove a contact from the segment. 77 | * 78 | * @param int $segmentId Segment ID 79 | * @param int $contactId Contact ID 80 | * 81 | * @return array|mixed 82 | */ 83 | public function removeContact($segmentId, $contactId) 84 | { 85 | return $this->makeRequest($this->endpoint.'/'.$segmentId.'/contact/'.$contactId.'/remove', [], 'POST'); 86 | } 87 | 88 | /** 89 | * Remove a lead from the segment. 90 | * 91 | * @deprecated 2.0.1, use addContact() instead 92 | * 93 | * @param int $id Segment ID 94 | * @param int $leadId Lead ID 95 | * 96 | * @return array|mixed 97 | */ 98 | public function removeLead($id, $leadId) 99 | { 100 | return $this->removeContact($id, $leadId); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /lib/Api/Smses.php: -------------------------------------------------------------------------------- 1 | makeRequest($this->endpoint.'/'.$id.'/contact/'.$contactId.'/send'); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/Api/Stages.php: -------------------------------------------------------------------------------- 1 | 'stages/$1/contact/add/$2', // 2.6.0 27 | 'stages/(.*?)/contact/(.*?)/remove' => 'stages/$1/contact/remove/$2', // 2.6.0 28 | ]; 29 | 30 | protected $searchCommands = [ 31 | 'ids', 32 | ]; 33 | 34 | /** 35 | * Add a contact to the stage. 36 | * 37 | * @param int $id Stage ID 38 | * @param int $contactId Contact ID 39 | * 40 | * @return array|mixed 41 | */ 42 | public function addContact($id, $contactId) 43 | { 44 | return $this->makeRequest($this->endpoint.'/'.$id.'/contact/'.$contactId.'/add', [], 'POST'); 45 | } 46 | 47 | /** 48 | * Remove a contact from the stage. 49 | * 50 | * @param int $id Stage ID 51 | * @param int $contactId Contact ID 52 | * 53 | * @return array|mixed 54 | */ 55 | public function removeContact($id, $contactId) 56 | { 57 | return $this->makeRequest($this->endpoint.'/'.$id.'/contact/'.$contactId.'/remove', [], 'POST'); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lib/Api/Stats.php: -------------------------------------------------------------------------------- 1 | $start, 36 | 'limit' => $limit, 37 | 'order' => $order, 38 | 'where' => $where, 39 | ]; 40 | 41 | $parameters = array_filter($parameters); 42 | 43 | $endpoint = $this->endpoint; 44 | if ($table) { 45 | $endpoint .= '/'.$table; 46 | } 47 | 48 | return $this->makeRequest($endpoint, $parameters); 49 | } 50 | 51 | public function delete($id) 52 | { 53 | return $this->actionNotSupported('delete'); 54 | } 55 | 56 | public function getList($search = '', $start = 0, $limit = 0, $orderBy = '', $orderByDir = 'ASC', $publishedOnly = false, $minimal = false) 57 | { 58 | return $this->actionNotSupported('getList'); 59 | } 60 | 61 | public function create(array $parameters) 62 | { 63 | return $this->actionNotSupported('create'); 64 | } 65 | 66 | public function getPublishedList($search = '', $start = 0, $limit = 0, $orderBy = '', $orderByDir = 'ASC') 67 | { 68 | return $this->actionNotSupported('getPublishedList'); 69 | } 70 | 71 | public function edit($id, array $parameters, $createIfNotExists = false) 72 | { 73 | return $this->actionNotSupported('edit'); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/Api/Tags.php: -------------------------------------------------------------------------------- 1 | actionNotSupported('edit'); 30 | } 31 | 32 | /** 33 | * @return array|mixed 34 | */ 35 | public function create(array $parameters) 36 | { 37 | if (!isset($parameters['file'])) { 38 | throw new \InvalidArgumentException('theme zip file must be set in parameters'); 39 | } 40 | 41 | return parent::create($parameters); 42 | } 43 | 44 | public function createBatch(array $parameters) 45 | { 46 | return $this->actionNotSupported('createBatch'); 47 | } 48 | 49 | public function editBatch(array $parameters, $createIfNotExists = false) 50 | { 51 | return $this->actionNotSupported('editBatch'); 52 | } 53 | 54 | public function deleteBatch(array $ids) 55 | { 56 | return $this->actionNotSupported('deleteBatch'); 57 | } 58 | 59 | /** 60 | * @return null 61 | */ 62 | public function getTemporaryFilepath() 63 | { 64 | return $this->temporaryFilePath ?: sys_get_temp_dir(); 65 | } 66 | 67 | /** 68 | * @param null $temporaryFilePath 69 | */ 70 | public function setTemporaryFilePath($temporaryFilePath) 71 | { 72 | $this->temporaryFilePath = $temporaryFilePath; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/Api/Tweets.php: -------------------------------------------------------------------------------- 1 | makeRequest($this->endpoint.'/self'); 45 | } 46 | 47 | /** 48 | * Get list of permissions for a user. 49 | * 50 | * @param int $id 51 | * @param string|array $permissions 52 | * 53 | * @return array|mixed 54 | */ 55 | public function checkPermission($id, $permissions) 56 | { 57 | return $this->makeRequest($this->endpoint.'/'.$id.'/permissioncheck', ['permissions' => $permissions], 'POST'); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lib/Api/Webhooks.php: -------------------------------------------------------------------------------- 1 | makeRequest($this->endpoint.'/triggers'); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/Auth/AbstractAuth.php: -------------------------------------------------------------------------------- 1 | client = $client; 42 | } 43 | 44 | /** 45 | * @param string $url 46 | * @param string $method 47 | */ 48 | abstract protected function prepareRequest($url, array $headers, array $parameters, $method, array $settings); 49 | 50 | /** 51 | * Enables debug mode. 52 | * 53 | * @return $this 54 | */ 55 | public function enableDebugMode() 56 | { 57 | $this->_debug = true; 58 | 59 | return $this; 60 | } 61 | 62 | /** 63 | * Returns $_SESSION['oauth']['debug'] if $this->_debug = true. 64 | * 65 | * @return array 66 | */ 67 | public function getDebugInfo() 68 | { 69 | return ($this->_debug && !empty($_SESSION['oauth']['debug'])) ? $_SESSION['oauth']['debug'] : []; 70 | } 71 | 72 | /** 73 | * Returns array of HTTP response headers. 74 | * 75 | * @return array 76 | */ 77 | public function getResponseHeaders() 78 | { 79 | return array_map( 80 | static function ($values) { 81 | return $values[0]; 82 | }, 83 | $this->_httpResponse->getHeaders() 84 | ); 85 | } 86 | 87 | /** 88 | * Returns the HTTP response. 89 | * 90 | * @return ResponseInterface 91 | */ 92 | public function getResponse() 93 | { 94 | return $this->_httpResponse; 95 | } 96 | 97 | /** 98 | * @throws UnexpectedResponseFormatException|Exception 99 | */ 100 | public function makeRequest($url, array $parameters = [], $method = 'GET', array $settings = []) 101 | { 102 | $this->log('makeRequest('.$url.', '.http_build_query($parameters).', '.$method.',...)'); 103 | 104 | [$url, $parameters] = $this->separateUrlParams($url, $parameters); 105 | 106 | // Make sure $method is capitalized for congruency 107 | $method = strtoupper($method); 108 | $headers = (isset($settings['headers']) && is_array($settings['headers'])) ? $settings['headers'] : []; 109 | 110 | [$headers, $parameters] = $this->prepareRequest($url, $headers, $parameters, $method, $settings); 111 | 112 | // Prepare parameters/body 113 | $body = null; 114 | if (in_array($method, ['POST', 'PUT', 'PATCH'])) { 115 | if (!empty($parameters['file']) && file_exists($parameters['file'])) { 116 | $elements = []; 117 | foreach ($parameters as $key => $value) { 118 | $elements[] = [ 119 | 'name' => $key, 120 | 'contents' => 'file' === $key ? Utils::tryFopen($value, 'r+') : $value, 121 | ]; 122 | } 123 | 124 | $body = new MultipartStream($elements); 125 | $headers[] = 'Content-Type: multipart/form-data; boundary='.$body->getBoundary(); 126 | } else { 127 | $body = Utils::streamFor(json_encode($parameters)); 128 | $headers[] = 'Content-Type: application/json'; 129 | } 130 | 131 | $this->log('Posted parameters = '.print_r($parameters, true)); 132 | } 133 | 134 | $query = $this->getQueryParameters(null !== $body, $parameters); 135 | $this->log('Query parameters = '.print_r($query, true)); 136 | 137 | // Create a query string for GET/DELETE requests 138 | if (count($query) > 0) { 139 | $queryGlue = false === strpos($url, '?') ? '?' : '&'; 140 | $url .= $queryGlue.http_build_query($query, '', '&'); 141 | $this->log('URL updated to '.$url); 142 | } 143 | 144 | $headers[] = 'Accept: application/json'; 145 | 146 | // Build request 147 | $request = new Request($method, $url); 148 | foreach ($headers as $header) { 149 | [$name, $value] = explode(':', $header, 2); 150 | $request = $request->withHeader(trim($name), trim($value)); 151 | } 152 | if ($body) { 153 | $request = $request->withBody($body); 154 | } 155 | 156 | // Send request 157 | $this->_httpResponse = $this->client->sendRequest($request); 158 | 159 | // Parse response 160 | $response = new Response($this->_httpResponse); 161 | 162 | if ($this->_debug) { 163 | $_SESSION['oauth']['debug']['returnedStatusCode'] = $response->getStatusCode(); 164 | $_SESSION['oauth']['debug']['returnedHeaders'] = $response->getHeaders(); 165 | $_SESSION['oauth']['debug']['returnedBody'] = $response->getBody(); 166 | } 167 | 168 | // Handle zip file response 169 | if ($response->isZip()) { 170 | return $response->saveToFile($settings['temporaryFilePath'] ?? sys_get_temp_dir()); 171 | } 172 | 173 | return $response->getDecodedBody(); 174 | } 175 | 176 | /** 177 | * @param bool $isPost 178 | * @param array $parameters 179 | * 180 | * @return array 181 | */ 182 | protected function getQueryParameters($isPost, $parameters) 183 | { 184 | return ($isPost) ? [] : (array) $parameters; 185 | } 186 | 187 | /** 188 | * @param string $message 189 | */ 190 | protected function log($message) 191 | { 192 | if ($this->_debug) { 193 | $_SESSION['oauth']['debug']['flow'][date('m-d H:i:s')][] = $message; 194 | } 195 | } 196 | 197 | /** 198 | * Separates parameters from base URL. 199 | * 200 | * @param string $url 201 | * @param array $params 202 | * 203 | * @return array 204 | */ 205 | protected function separateUrlParams($url, $params) 206 | { 207 | $a = parse_url($url); 208 | 209 | if (!empty($a['query'])) { 210 | parse_str($a['query'], $qparts); 211 | $cleanParams = []; 212 | foreach ($qparts as $k => $v) { 213 | $cleanParams[$k] = $v ? $v : ''; 214 | } 215 | $params = array_merge($params, $cleanParams); 216 | $urlParts = explode('?', $url, 2); 217 | $url = $urlParts[0]; 218 | } 219 | 220 | return [$url, $params]; 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /lib/Auth/ApiAuth.php: -------------------------------------------------------------------------------- 1 | client = $client ?: Psr18ClientDiscovery::find(); 27 | } 28 | 29 | /** 30 | * Get an API Auth object. 31 | * 32 | * @param array $parameters 33 | * @param string $authMethod 34 | * 35 | * @return AuthInterface 36 | * 37 | * @deprecated 38 | */ 39 | public static function initiate($parameters = [], $authMethod = 'OAuth') 40 | { 41 | $object = new self(); 42 | 43 | return $object->newAuth($parameters, $authMethod); 44 | } 45 | 46 | /** 47 | * Get an API Auth object. 48 | * 49 | * @param array $parameters 50 | * @param string $authMethod 51 | * 52 | * @return AuthInterface 53 | */ 54 | public function newAuth($parameters = [], $authMethod = 'OAuth') 55 | { 56 | $class = 'Mautic\\Auth\\'.$authMethod; 57 | $authObject = new $class($this->client); 58 | 59 | $reflection = new \ReflectionMethod($class, 'setup'); 60 | $pass = []; 61 | 62 | foreach ($reflection->getParameters() as $param) { 63 | if (isset($parameters[$param->getName()])) { 64 | $pass[] = $parameters[$param->getName()]; 65 | } else { 66 | $pass[] = null; 67 | } 68 | } 69 | 70 | $reflection->invokeArgs($authObject, $pass); 71 | 72 | return $authObject; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/Auth/AuthInterface.php: -------------------------------------------------------------------------------- 1 | newAuth() will accept an array of Auth settings 20 | | $settings = array( 21 | | 'AuthMethod' => 'BasicAuth' // Must be one of 'OAuth' or 'BasicAuth' 22 | | 'userName' => '', // The username for authentication; Best practise would be to set up a new user for each external site 23 | | 'password' => '', // Make this a Long passPhrase e.g. (Try.!wE4.And.*@ws4.Guess.!a4911.This.*-+1.Sucker.!) 24 | | 'apiUrl' => '', // NOTE: Required for Unit tests; *must* contain a valid url 25 | | ); 26 | | 27 | | // Initiate the auth object 28 | | $initAuth = new ApiAuth(); 29 | | $auth = $initAuth->newAuth($settings, $settings['AuthMethod']); 30 | | 31 | |-------------------------------------------------------------------------- 32 | | Basic API Usage 33 | |-------------------------------------------------------------------------- 34 | | 35 | | To use, just pass the auth object to the Api context you are creating. 36 | | 37 | | use Mautic\MauticApi; 38 | | 39 | | // Get a Contact context 40 | | $api = new MauticApi(); 41 | | $contactApi = $api->newApi('contacts', $auth, $settings['apiUrl']); 42 | | 43 | | // Get Contact list 44 | | $results = $contactApi->getList(); 45 | | 46 | | Note: If the credentials are incorrect an error response will be returned: 47 | | array('errors' => [[ 48 | | 'code' => 403, 49 | | 'message' => 'access_denied: OAuth2 authentication required' ) 50 | | ]] 51 | | 52 | */ 53 | 54 | namespace Mautic\Auth; 55 | 56 | use Mautic\Exception\RequiredParameterMissingException; 57 | 58 | /** 59 | * Basic Authentication Client mashed together by MarkLL. 60 | */ 61 | class BasicAuth extends AbstractAuth 62 | { 63 | /** 64 | * Password associated with Username. 65 | * 66 | * @var string 67 | */ 68 | private $password; 69 | 70 | /** 71 | * Username or email, basically the Login Identifier. 72 | * 73 | * @var string 74 | */ 75 | private $userName; 76 | 77 | public function isAuthorized() 78 | { 79 | return !empty($this->userName) && !empty($this->password); 80 | } 81 | 82 | /** 83 | * @param string $userName The username to use for Authentication *Required* 84 | * @param string $password The Password to use *Required* 85 | * 86 | * @throws RequiredParameterMissingException 87 | */ 88 | public function setup($userName, $password) 89 | { 90 | // we MUST have the username and password. No Blanks allowed! 91 | // 92 | // remove blanks else Empty doesn't work 93 | $userName = trim($userName); 94 | $password = trim($password); 95 | 96 | if (empty($userName) || empty($password)) { 97 | // Throw exception if the required parameters were not found 98 | $this->log('parameters did not include username and/or password'); 99 | throw new RequiredParameterMissingException('One or more required parameters was not supplied. Both userName and password required!'); 100 | } 101 | 102 | $this->userName = $userName; 103 | $this->password = $password; 104 | } 105 | 106 | /** 107 | * @param string $url 108 | * @param string $method 109 | * 110 | * @return array 111 | */ 112 | protected function prepareRequest($url, array $headers, array $parameters, $method, array $settings) 113 | { 114 | // Set Basic Auth parameters/headers 115 | $headers = array_merge($headers, [$this->buildAuthorizationHeader(), 'Expect:']); 116 | 117 | return [$headers, $parameters]; 118 | } 119 | 120 | /** 121 | * Build header for Basic Authentication. 122 | * 123 | * @return string 124 | */ 125 | private function buildAuthorizationHeader() 126 | { 127 | /* 128 | |-------------------------------------------------------------------------- 129 | | Authorization Header 130 | |-------------------------------------------------------------------------- 131 | | 132 | | Authorization is passed in the Header using Basic Authentication. 133 | | 134 | | Basically we take the username and password and seperate it with a 135 | | colon (:) and base 64 encode it: 136 | | 137 | | 'Authorization: Basic username:password' 138 | | 139 | | ==> with base64 encoding of the username and password 140 | | 141 | | 'Authorization: Basic dXNlcjpwYXNzd29yZA==' 142 | | 143 | */ 144 | return 'Authorization: Basic '.base64_encode($this->userName.':'.$this->password); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /lib/Exception/AbstractApiException.php: -------------------------------------------------------------------------------- 1 | authUrl = $authUrl; 30 | } 31 | 32 | /** 33 | * @return string 34 | */ 35 | public function getAuthUrl() 36 | { 37 | return $this->authUrl; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/Exception/ContextNotFoundException.php: -------------------------------------------------------------------------------- 1 | response = $response; 34 | 35 | if (empty($message)) { 36 | // Use message appropriate to the subclass with late binding 37 | $message = self::DEFAULT_MESSAGE; 38 | } 39 | 40 | $message .= "\n\nResponse: "; 41 | 42 | // Attach first 1000 characters of the body 43 | if (mb_strlen($response->getBody()) > 1000) { 44 | $message .= mb_substr($response->getBody(), 0, 1000).'...'; 45 | } else { 46 | $message .= $response->getBody(); 47 | } 48 | 49 | parent::__construct($message, $code, $previous); 50 | } 51 | 52 | /** 53 | * @return Response 54 | */ 55 | public function getResponse() 56 | { 57 | return $this->response; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lib/MauticApi.php: -------------------------------------------------------------------------------- 1 | select[] = $column; 42 | } 43 | 44 | /** 45 | * @param string $column 46 | * @param string $dir 47 | */ 48 | public function addOrder($column, $dir = 'asc') 49 | { 50 | $this->order[] = [ 51 | 'col' => $column, 52 | 'dir' => $dir, 53 | ]; 54 | } 55 | 56 | public function addWhere(?WhereBuilder $whereBuilder = null) 57 | { 58 | if (null === $whereBuilder) { 59 | if (null === self::$whereBuilder) { 60 | return; 61 | } 62 | 63 | $whereBuilder = self::$whereBuilder; 64 | } 65 | 66 | $this->where = array_merge($this->where, $whereBuilder->getClauses()); 67 | 68 | $this->resetWhereBuilder(); 69 | } 70 | 71 | /** 72 | * @param bool $new 73 | * 74 | * @return WhereBuilder 75 | */ 76 | public function getWhereBuilder($new = false) 77 | { 78 | if ($new) { 79 | return new WhereBuilder(); 80 | } 81 | 82 | if (null == self::$whereBuilder) { 83 | $this->resetWhereBuilder(); 84 | } 85 | 86 | return self::$whereBuilder; 87 | } 88 | 89 | /** 90 | * @return WhereBuilder 91 | */ 92 | public function expr() 93 | { 94 | return $this->getWhereBuilder(true); 95 | } 96 | 97 | /** 98 | * @return array 99 | */ 100 | public function getSelect() 101 | { 102 | return $this->select; 103 | } 104 | 105 | /** 106 | * @param array $select 107 | * 108 | * @return QueryBuilder 109 | */ 110 | public function setSelect($select) 111 | { 112 | $this->select = $select; 113 | 114 | return $this; 115 | } 116 | 117 | /** 118 | * @return array 119 | */ 120 | public function getOrder() 121 | { 122 | return $this->order; 123 | } 124 | 125 | /** 126 | * @param array $order 127 | * 128 | * @return QueryBuilder 129 | */ 130 | public function setOrder($order) 131 | { 132 | $this->order = $order; 133 | 134 | return $this; 135 | } 136 | 137 | /** 138 | * @return array 139 | */ 140 | public function getWhere() 141 | { 142 | // Add clauses from static::$whereBuilder 143 | $this->addWhere(); 144 | 145 | return $this->where; 146 | } 147 | 148 | /** 149 | * @param array $where 150 | * 151 | * @return QueryBuilder 152 | */ 153 | public function setWhere($where) 154 | { 155 | $this->where = $where; 156 | 157 | return $this; 158 | } 159 | 160 | protected function resetWhereBuilder() 161 | { 162 | self::$whereBuilder = new WhereBuilder(); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /lib/QueryBuilder/WhereBuilder.php: -------------------------------------------------------------------------------- 1 | composite = $composite; 37 | } 38 | 39 | /** 40 | * @return array 41 | */ 42 | public function getClauses() 43 | { 44 | return $this->clauses; 45 | } 46 | 47 | /** 48 | * @param string $col 49 | * 50 | * @return $this 51 | */ 52 | public function eq($col, $val) 53 | { 54 | $this->addClause($col, __FUNCTION__, $val); 55 | 56 | return $this; 57 | } 58 | 59 | /** 60 | * @param string $col 61 | * 62 | * @return $this 63 | */ 64 | public function neq($col, $val) 65 | { 66 | $this->addClause($col, __FUNCTION__, $val); 67 | 68 | return $this; 69 | } 70 | 71 | /** 72 | * @param string $col 73 | * 74 | * @return $this 75 | */ 76 | public function lt($col, $val) 77 | { 78 | $this->addClause($col, __FUNCTION__, $val); 79 | 80 | return $this; 81 | } 82 | 83 | /** 84 | * @param string $col 85 | * 86 | * @return $this 87 | */ 88 | public function lte($col, $val) 89 | { 90 | $this->addClause($col, __FUNCTION__, $val); 91 | 92 | return $this; 93 | } 94 | 95 | /** 96 | * @param string $col 97 | * 98 | * @return $this 99 | */ 100 | public function gt($col, $val) 101 | { 102 | $this->addClause($col, __FUNCTION__, $val); 103 | 104 | return $this; 105 | } 106 | 107 | /** 108 | * @param string $col 109 | * 110 | * @return $this 111 | */ 112 | public function gte($col, $val) 113 | { 114 | $this->addClause($col, __FUNCTION__, $val); 115 | 116 | return $this; 117 | } 118 | 119 | /** 120 | * @param string $col 121 | * 122 | * @return $this 123 | */ 124 | public function like($col, $val) 125 | { 126 | $this->addClause($col, __FUNCTION__, $val); 127 | 128 | return $this; 129 | } 130 | 131 | /** 132 | * @param string $col 133 | * 134 | * @return $this 135 | */ 136 | public function notLike($col, $val) 137 | { 138 | $this->addClause($col, __FUNCTION__, $val); 139 | 140 | return $this; 141 | } 142 | 143 | /** 144 | * @param string $col 145 | * 146 | * @return $this 147 | */ 148 | public function in($col, array $val) 149 | { 150 | $this->addClause($col, __FUNCTION__, $val); 151 | 152 | return $this; 153 | } 154 | 155 | /** 156 | * @param string $col 157 | * 158 | * @return $this 159 | */ 160 | public function notIn($col, array $val) 161 | { 162 | $this->addClause($col, __FUNCTION__, $val); 163 | 164 | return $this; 165 | } 166 | 167 | /** 168 | * @param string $col 169 | * 170 | * @return $this 171 | */ 172 | public function between($col, $val1, $val2) 173 | { 174 | $this->addClause($col, __FUNCTION__, [$val1, $val2]); 175 | 176 | return $this; 177 | } 178 | 179 | /** 180 | * @param string $col 181 | * 182 | * @return $this 183 | */ 184 | public function notBetween($col, $val1, $val2) 185 | { 186 | $this->addClause($col, __FUNCTION__, [$val1, $val2]); 187 | 188 | return $this; 189 | } 190 | 191 | /** 192 | * @param string $col 193 | * 194 | * @return $this 195 | */ 196 | public function isNull($col) 197 | { 198 | $this->addClause($col, __FUNCTION__); 199 | 200 | return $this; 201 | } 202 | 203 | /** 204 | * @param string $col 205 | * 206 | * @return $this 207 | */ 208 | public function isNotNull($col) 209 | { 210 | $this->addClause($col, __FUNCTION__); 211 | 212 | return $this; 213 | } 214 | 215 | /** 216 | * @param string $col 217 | * 218 | * @return $this 219 | */ 220 | public function isEmpty($col) 221 | { 222 | $this->addClause($col, __FUNCTION__); 223 | 224 | return $this; 225 | } 226 | 227 | /** 228 | * @param string $col 229 | * 230 | * @return $this 231 | */ 232 | public function isNotEmpty($col) 233 | { 234 | $this->addClause($col, __FUNCTION__); 235 | 236 | return $this; 237 | } 238 | 239 | /** 240 | * @return self 241 | */ 242 | public function andX() 243 | { 244 | return $this->x('andX', func_get_args()); 245 | } 246 | 247 | /** 248 | * @return WhereBuilder 249 | */ 250 | public function orX() 251 | { 252 | return $this->x('orX', func_get_args()); 253 | } 254 | 255 | /** 256 | * @param array|self $clause 257 | */ 258 | public function add($clause) 259 | { 260 | if (is_array($clause)) { 261 | $this->clauses[] = $clause; 262 | } elseif ($clause instanceof self) { 263 | switch ($composite = $clause->getComposite()) { 264 | case 'andX': 265 | case 'orX': 266 | $this->add( 267 | [ 268 | 'col' => null, 269 | 'expr' => $composite, 270 | 'val' => $clause->getClauses(), 271 | ] 272 | ); 273 | break; 274 | default: 275 | $this->clauses = array_merge($this->clauses, $clause->getClauses()); 276 | } 277 | } 278 | } 279 | 280 | /** 281 | * @return null 282 | */ 283 | public function getComposite() 284 | { 285 | return $this->composite; 286 | } 287 | 288 | /** 289 | * @param string $col 290 | * @param string $expr 291 | * @param null $val 292 | */ 293 | private function addClause($col, $expr, $val = null) 294 | { 295 | $this->clauses[] = [ 296 | 'col' => $col, 297 | 'expr' => $expr, 298 | 'val' => $val, 299 | ]; 300 | } 301 | 302 | /** 303 | * @param string $composite 304 | * @param array $clauses 305 | * 306 | * @return $this|WhereBuilder 307 | */ 308 | private function x($composite, $clauses) 309 | { 310 | $builder = new self($composite); 311 | 312 | if ($clauses) { 313 | foreach ($clauses as $x) { 314 | $builder->add($x); 315 | } 316 | 317 | $this->add($builder); 318 | 319 | return $this; 320 | } 321 | 322 | return $builder; 323 | } 324 | } 325 | -------------------------------------------------------------------------------- /lib/Response.php: -------------------------------------------------------------------------------- 1 | response = $response; 35 | $this->body = (string) $this->response->getBody(); 36 | $this->validate(); 37 | } 38 | 39 | /** 40 | * @return int 41 | */ 42 | public function getStatusCode() 43 | { 44 | return $this->response->getStatusCode(); 45 | } 46 | 47 | /** 48 | * @return array 49 | */ 50 | public function getHeaders() 51 | { 52 | return $this->response->getHeaders(); 53 | } 54 | 55 | /** 56 | * @return string 57 | */ 58 | public function getBody() 59 | { 60 | return $this->body; 61 | } 62 | 63 | /** 64 | * @return array 65 | */ 66 | public function getDecodedBody() 67 | { 68 | try { 69 | $parsed = $this->decodeFromJson(); 70 | } catch (UnexpectedResponseFormatException $e) { 71 | $parsed = $this->decodeFromUrlParams(); 72 | } 73 | 74 | return $parsed; 75 | } 76 | 77 | /** 78 | * @return array 79 | * 80 | * @throws UnexpectedResponseFormatException 81 | */ 82 | public function decodeFromJson() 83 | { 84 | $parsed = json_decode($this->body, true); 85 | 86 | if (is_null($parsed)) { 87 | throw new UnexpectedResponseFormatException($this); 88 | } 89 | 90 | return $parsed; 91 | } 92 | 93 | /** 94 | * @return array 95 | * 96 | * @throws UnexpectedResponseFormatException 97 | */ 98 | public function decodeFromUrlParams() 99 | { 100 | if (false !== strpos($this->body, '=')) { 101 | parse_str($this->body, $parsed); 102 | } 103 | 104 | if (empty($parsed)) { 105 | throw new UnexpectedResponseFormatException($this); 106 | } 107 | 108 | return $parsed; 109 | } 110 | 111 | /** 112 | * @return bool 113 | */ 114 | public function isZip() 115 | { 116 | return $this->response->hasHeader('Content-Type') && 'application/zip' === $this->response->getHeader('Content-Type')[0]; 117 | } 118 | 119 | /** 120 | * @return bool 121 | */ 122 | public function isHtml() 123 | { 124 | return '<' === substr(trim($this->body), 0, 1); 125 | } 126 | 127 | /** 128 | * @param string $path 129 | * 130 | * @return array 131 | */ 132 | public function saveToFile($path) 133 | { 134 | if (!file_exists($path)) { 135 | if (!@mkdir($path) && !is_dir($path)) { 136 | throw new \Exception('Cannot create directory '.$path); 137 | } 138 | } 139 | $file = tempnam($path, 'mautic_api_'); 140 | 141 | if (!is_writable($file)) { 142 | throw new \Exception($file.' is not writable'); 143 | } 144 | 145 | if (!$handle = fopen($file, 'w')) { 146 | throw new \Exception('Cannot open file '.$file); 147 | } 148 | 149 | if (false === fwrite($handle, $this->body)) { 150 | throw new \Exception('Cannot write into file '.$file); 151 | } 152 | 153 | fclose($handle); 154 | 155 | return [ 156 | 'file' => $file, 157 | ]; 158 | } 159 | 160 | /** 161 | * @throws UnexpectedResponseFormatException 162 | */ 163 | private function validate() 164 | { 165 | if (!in_array($this->response->getStatusCode(), [200, 201])) { 166 | $message = 'The response has unexpected status code ('.$this->response->getStatusCode().').'; 167 | throw new UnexpectedResponseFormatException($this, $message, $this->response->getStatusCode()); 168 | } 169 | 170 | if ($this->isHtml()) { 171 | throw new UnexpectedResponseFormatException($this); 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: 1 3 | paths: 4 | - lib 5 | - tests -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | lib 6 | 7 | 8 | 9 | 10 | tests 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/Api/AbstractCustomFieldsTest.php: -------------------------------------------------------------------------------- 1 | assertFalse( 24 | empty($item['fields']['all']), 25 | '[fields][all] is missing:'.var_export($item, true) 26 | ); 27 | 28 | if (isset($item['fields']['all'][$itemProp])) { 29 | $item = $item['fields']['all']; 30 | } 31 | 32 | $this->assertTrue(isset($item[$itemProp]), $itemProp.' doesn\'t exist in the response: '.var_export($item, true)); 33 | $this->assertEquals($item[$itemProp], $itemVal); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/Api/AssetsTest.php: -------------------------------------------------------------------------------- 1 | api = $this->getContext('assets'); 23 | $this->testPayload = [ 24 | 'title' => 'Mautic Logo sent as a API request', 25 | 'storageLocation' => 'remote', 26 | 'file' => 'https://www.mautic.org/media/logos/logo/Mautic_Logo_DB.pdf', 27 | ]; 28 | 29 | if ('4' == $this->mauticVersion) { 30 | $this->mediaFolder = 'assets'; 31 | } 32 | } 33 | 34 | public function testGetList() 35 | { 36 | $this->standardTestGetList(); 37 | } 38 | 39 | public function testGetListOfSpecificIds() 40 | { 41 | $this->standardTestGetListOfSpecificIds(); 42 | } 43 | 44 | public function testCreateWithLocalFileGetAndDelete() 45 | { 46 | // Upload a testing file 47 | $apiFiles = $this->getContext('files'); 48 | $apiFiles->setFolder($this->mediaFolder); 49 | $fileRequest = [ 50 | 'file' => dirname(__DIR__).'/mauticlogo.png', 51 | ]; 52 | $response = $apiFiles->create($fileRequest); 53 | $this->assertErrors($response); 54 | $file = $response['file']; 55 | 56 | // Build local file payload 57 | $testPayload = $this->testPayload; 58 | $testPayload['storageLocation'] = 'local'; 59 | $testPayload['file'] = $file['name']; 60 | 61 | // Create Asset 62 | $response = $this->api->create($testPayload); 63 | $this->assertPayload($response, $testPayload); 64 | 65 | $response = $this->api->get($response[$this->api->itemName()]['id']); 66 | $this->assertPayload($response, $testPayload); 67 | 68 | // Delete Asset 69 | $response = $this->api->delete($response[$this->api->itemName()]['id']); 70 | $this->assertErrors($response); 71 | } 72 | 73 | public function testCreateWithRemoteFileGetAndDelete() 74 | { 75 | $this->standardTestCreateGetAndDelete(); 76 | } 77 | 78 | public function testBatchEndpoints() 79 | { 80 | $this->standardTestBatchEndpoints(); 81 | } 82 | 83 | public function testEditPut() 84 | { 85 | $this->standardTestEditPut(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /tests/Api/Auth/AbstractAuthTest.php: -------------------------------------------------------------------------------- 1 | config = include __DIR__.'/../../local.config.php'; 26 | } 27 | 28 | public function test404Response() 29 | { 30 | $auth = $this->getMockForAbstractClass(AbstractAuth::class, [new Client()]); 31 | $this->expectException(UnexpectedResponseFormatException::class); 32 | $auth->makeRequest('https://github.com/mautic/api-library/this-page-does-not-exist'); 33 | } 34 | 35 | public function testHtmlResponse() 36 | { 37 | $auth = $this->getMockForAbstractClass(AbstractAuth::class, [new Client()]); 38 | $this->expectException(UnexpectedResponseFormatException::class); 39 | $auth->makeRequest($this->config['baseUrl']); 40 | } 41 | 42 | public function testJsonResponse() 43 | { 44 | $auth = $this->getMockForAbstractClass(AbstractAuth::class, [new Client()]); 45 | try { 46 | $auth->makeRequest($this->config['apiUrl'].'contacts'); 47 | self::fail('This should not happen, as the API does not have the authentication.'); 48 | } catch (UnexpectedResponseFormatException $exception) { 49 | $body = $exception->getResponse()->getBody(); 50 | try { 51 | $response = json_decode($body, true, 512, JSON_THROW_ON_ERROR); 52 | } catch (\JsonException $e) { 53 | if ('' === $body) { 54 | $body = '(empty string)'; 55 | } 56 | 57 | self::fail('Mautic returned wrong json response: '.$body.'. JSON exception: '.$e->getMessage()); 58 | } 59 | $this->assertIsArray($response, $body); 60 | $this->assertGreaterThan(0, count($response)); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tests/Api/Auth/BasicAuthTest.php: -------------------------------------------------------------------------------- 1 | configFile = __DIR__.'/../../local.config.php'; 26 | } 27 | 28 | /** 29 | * Verify that the error handling in setup method is working 30 | * - No Username. 31 | */ 32 | public function testParameterExceptionErrorNoUserName() 33 | { 34 | $this->expectException(RequiredParameterMissingException::class); 35 | $this->expectExceptionCode(500); 36 | 37 | // This should throw an error becuse the userName is missing 38 | $apiAuth = new ApiAuth(); 39 | $auth = $apiAuth->newAuth(['password'=>'********'], 'BasicAuth'); 40 | } 41 | 42 | /** 43 | * Verify that the error handling in setup method is working 44 | * - No Password. 45 | */ 46 | public function testParameterExceptionErrorNoPassword() 47 | { 48 | $this->expectException(RequiredParameterMissingException::class); 49 | $this->expectExceptionCode(500); 50 | 51 | // This should throw an error becuse the password is missing 52 | $api = new ApiAuth(); 53 | $auth = $api->newAuth(['userName'=>'anyolduser'], 'BasicAuth'); 54 | } 55 | 56 | /** 57 | * Verify that the error handling in setup method is working 58 | * - Empty Username. 59 | */ 60 | public function testParameterExceptionErrorEmptyUserName() 61 | { 62 | $this->expectException(RequiredParameterMissingException::class); 63 | $this->expectExceptionCode(500); 64 | 65 | // This should throw an error becuse the userName is empty - test blanks 66 | $apiAuth = new ApiAuth(); 67 | $auth = $apiAuth->newAuth(['userName'=>' ', 'password'=>'********'], 'BasicAuth'); 68 | } 69 | 70 | /** 71 | * Verify that the error handling in setup method is working 72 | * - Empty password. 73 | */ 74 | public function testParameterExceptionErrorEmptyPassword() 75 | { 76 | $this->expectException(RequiredParameterMissingException::class); 77 | $this->expectExceptionCode(500); 78 | 79 | // This should throw an error because the password is empty - test blanks 80 | $apiAuth = new ApiAuth(); 81 | $auth = $apiAuth->newAuth(['userName'=>'admin', 'password'=>' '], 'BasicAuth'); 82 | } 83 | 84 | /** 85 | * Ensure the Config has the correct settings. 86 | */ 87 | public function testConfigReady() 88 | { 89 | $this->assertTrue(file_exists($this->configFile), 'Cannot find local.config.php!'); 90 | 91 | // get local config 92 | $config = include $this->configFile; 93 | 94 | // userName & password - ! empty 95 | $toCheck = ['userName', 'password']; 96 | foreach ($toCheck as $toTest) { 97 | $this->assertTrue(isset($config[$toTest]), $toTest.' Check failed. Check '.$toTest.' in local.config.php.'); 98 | $this->assertTrue(!empty($config[$toTest]), $toTest.' must contain a value. Check '.$toTest.' in local.config.php.'); 99 | } 100 | } 101 | 102 | /** 103 | * @depends testConfigReady 104 | */ 105 | public function testPublicInterface() 106 | { 107 | $config = include $this->configFile; 108 | $apiAuth = new ApiAuth(); 109 | $auth = $apiAuth->newAuth($config, 'BasicAuth'); 110 | 111 | $this->assertTrue($auth->isAuthorized(), 'Authorization failed. Check credentials in local.config.php.'); 112 | } 113 | 114 | /** 115 | * @depends testConfigReady 116 | */ 117 | public function testGetList() 118 | { 119 | $this->api = $this->getContext('contactFields'); 120 | $this->standardTestGetList(); 121 | } 122 | 123 | /** 124 | * Ignore this. 125 | */ 126 | public function testSearchCommands() 127 | { 128 | $this->markTestSkipped('Inherited method but not applicable'); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /tests/Api/CategoriesTest.php: -------------------------------------------------------------------------------- 1 | api = $this->getContext('categories'); 19 | $this->testPayload = [ 20 | 'title' => 'test', 21 | 'bundle' => 'asset', 22 | ]; 23 | } 24 | 25 | public function testGetList() 26 | { 27 | $this->standardTestGetList(); 28 | } 29 | 30 | public function testGetListOfSpecificIds() 31 | { 32 | $this->standardTestGetListOfSpecificIds(); 33 | } 34 | 35 | public function testCreateGetAndDelete() 36 | { 37 | // Create category 38 | $response = $this->api->create($this->testPayload); 39 | $this->assertErrors($response); 40 | $this->assertPayload($response, $this->testPayload); 41 | $categoryId = $response[$this->api->itemName()]['id']; 42 | 43 | // GET category 44 | $response = $this->api->get($categoryId); 45 | $this->assertErrors($response); 46 | $this->assertPayload($response, $this->testPayload); 47 | 48 | // Add an asset to this category 49 | $assetApi = $this->getContext('assets'); 50 | $assetPayload = [ 51 | 'title' => 'Mautic Logo sent as a API request', 52 | 'storageLocation' => 'remote', 53 | 'file' => 'https://www.mautic.org/media/logos/logo/Mautic_Logo_DB.pdf', 54 | 'category' => $categoryId, 55 | ]; 56 | 57 | // Create Asset 58 | $assetResponse = $assetApi->create($assetPayload); 59 | $assetCategory = $assetResponse[$assetApi->itemName()]['category']; 60 | $this->assertEquals($categoryId, $assetCategory['id']); 61 | $this->assertErrors($assetPayload); 62 | 63 | // Delete asset 64 | $response = $assetApi->delete($assetResponse[$assetApi->itemName()]['id']); 65 | $this->assertErrors($response); 66 | 67 | // Delete category 68 | $response = $this->api->delete($categoryId); 69 | $this->assertErrors($response); 70 | 71 | // Expect an error when assigning a non existing category when creating a new asset 72 | $assetResponse = $assetApi->create($assetPayload); 73 | $this->assertStringContainsString("Category $categoryId does not exist", $assetResponse['errors'][0]['message']); 74 | } 75 | 76 | public function testEditPatch() 77 | { 78 | $editTo = [ 79 | 'title' => 'test2', 80 | 'bundle' => 'asset', 81 | ]; 82 | $this->standardTestEditPatch($editTo); 83 | } 84 | 85 | public function testEditPut() 86 | { 87 | $this->standardTestEditPut(); 88 | } 89 | 90 | public function testBatchEndpoints() 91 | { 92 | $this->standardTestBatchEndpoints(); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /tests/Api/CompaniesTest.php: -------------------------------------------------------------------------------- 1 | api = $this->getContext('companies'); 19 | $this->testPayload = [ 20 | 'companyname' => 'test', 21 | 'companyemail' => 'test@company.com', 22 | 'companycity' => 'Raleigh', 23 | ]; 24 | } 25 | 26 | public function testGetList() 27 | { 28 | $this->standardTestGetList(); 29 | } 30 | 31 | public function testCreateGetAndDelete() 32 | { 33 | $this->standardTestCreateGetAndDelete(); 34 | } 35 | 36 | public function testEditPatch() 37 | { 38 | $editTo = [ 39 | 'companyname' => 'test2', 40 | ]; 41 | $this->standardTestEditPatch($editTo); 42 | } 43 | 44 | public function testEditPut() 45 | { 46 | $this->standardTestEditPut(); 47 | } 48 | 49 | public function testAddAndRemove() 50 | { 51 | // Create contact 52 | $contactsContext = $this->getContext('contacts'); 53 | $response = $contactsContext->create(['firstname' => 'API segments test']); 54 | $this->assertErrors($response); 55 | $contact = $response['contact']; 56 | 57 | // Create company 58 | $response = $this->api->create($this->testPayload); 59 | $this->assertPayload($response); 60 | $company = $response[$this->api->itemName()]; 61 | 62 | // Add the contact to the company 63 | $response = $this->api->addContact($company['id'], $contact['id']); 64 | $this->assertErrors($response); 65 | $this->assertSuccess($response); 66 | 67 | // Test get contact companies API endpoint 68 | $contactContext = $this->getContext('contacts'); 69 | $response = $contactContext->getContactCompanies($contact['id']); 70 | $this->assertErrors($response); 71 | $this->assertEquals($response['total'], 1); 72 | $this->assertFalse(empty($response['companies'])); 73 | 74 | // Remove the contact from the company 75 | $response = $this->api->removeContact($company['id'], $contact['id']); 76 | $this->assertErrors($response); 77 | $this->assertSuccess($response); 78 | 79 | // Delete the contact and the segment 80 | $response = $contactsContext->delete($contact['id']); 81 | $this->assertErrors($response); 82 | $response = $this->api->delete($company['id']); 83 | $this->assertErrors($response); 84 | } 85 | 86 | public function testBatchEndpoints() 87 | { 88 | $batch = []; 89 | 90 | // Company name must be unique 91 | for ($i = 0; $i < 3; ++$i) { 92 | $company = $this->testPayload; 93 | $company['companyname'] = 'test'.$i; 94 | $batch[] = $company; 95 | } 96 | 97 | $this->standardTestBatchEndpoints($batch); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /tests/Api/CompanyFieldTest.php: -------------------------------------------------------------------------------- 1 | api = $this->getContext('companyFields'); 23 | $this->testPayload = [ 24 | 'label' => $this->prefix.'API test field', 25 | 'type' => 'text', 26 | ]; 27 | } 28 | 29 | protected function assertFieldPayloadList($response) 30 | { 31 | if (!empty($response[$this->api->listName()])) { 32 | foreach ($response[$this->api->listName()] as $item) { 33 | $this->assertSame('company', $item['object'], 'This field must be object of company '.print_r($item, true)); 34 | } 35 | } 36 | } 37 | 38 | // Methods inherited from ContactFieldsTest 39 | 40 | // Except following, ignore them 41 | public function testBooleanField() 42 | { 43 | $this->markTestSkipped('Inherited method but not applicable'); 44 | } 45 | 46 | public function testDefaultFieldValue() 47 | { 48 | $this->markTestSkipped('Inherited method but not applicable'); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/Api/ContactFieldsTest.php: -------------------------------------------------------------------------------- 1 | api = $this->getContext('contactFields'); 21 | $this->testPayload = [ 22 | 'label' => $this->prefix.'API test field', 23 | 'type' => 'text', 24 | ]; 25 | } 26 | 27 | protected function assertPayloadList($response) 28 | { 29 | parent::assertPayloadList($response); 30 | $this->assertFieldPayloadList($response); 31 | } 32 | 33 | protected function assertFieldPayloadList($response) 34 | { 35 | if (!empty($response[$this->api->listName()])) { 36 | foreach ($response[$this->api->listName()] as $item) { 37 | $this->assertSame('lead', $item['object'], 'This field must be object of lead '.print_r($item, true)); 38 | } 39 | } 40 | } 41 | 42 | public function testGetList() 43 | { 44 | $this->standardTestGetList(); 45 | } 46 | 47 | public function testCreateGetAndDelete() 48 | { 49 | $this->standardTestCreateGetAndDelete(); 50 | } 51 | 52 | public function testCreateGetAndDeleteOfLookupField() 53 | { 54 | $lookupField = [ 55 | 'label' => $this->prefix.'API test lookup field', 56 | 'type' => 'lookup', 57 | 'properties' => [ 58 | 'list' => [ 59 | 'Mr', 60 | 'Mrs', 61 | 'Miss', 62 | ], 63 | ], 64 | ]; 65 | 66 | $this->standardTestCreateGetAndDelete($lookupField); 67 | } 68 | 69 | public function testBooleanField() 70 | { 71 | // Create a testing contact 72 | $contactContext = $this->getContext('contacts'); 73 | $response = $contactContext->create(['firstname' => 'Boolean Field', 'lastname' => 'API test']); 74 | $this->assertErrors($response); 75 | $contact = $response['contact']; 76 | 77 | $possibleValues = [1 => 1, 0 => 0, 'yes' => 1, 'no' => 0, 'true' => 1, 'false' => 0]; 78 | $boolField = [ 79 | 'label' => $this->prefix.'API test Boolean field', 80 | 'type' => 'boolean', 81 | 'properties' => [ 82 | 'no' => 'No', 83 | 'yes' => 'Yes', 84 | ], 85 | ]; 86 | 87 | // Create the Boolean field 88 | $response = $this->api->create($boolField); 89 | $this->assertErrors($response); 90 | $field = $response[$this->api->itemName()]; 91 | 92 | // Test if the Boolean value gets updated with test values 93 | foreach ($possibleValues as $value => $boolValue) { 94 | $response = $contactContext->edit($contact['id'], [$field['alias'] => $value]); 95 | $this->assertErrors($response); 96 | $this->assertTrue(isset($response['contact']['fields']['all'][$field['alias']]), $field['alias'].' does not exist in the field list'); 97 | $this->assertEquals($response['contact']['fields']['all'][$field['alias']], $boolValue); 98 | } 99 | 100 | // Clean after youself 101 | $response = $this->api->delete($field['id']); 102 | $this->assertErrors($response); 103 | $response = $contactContext->delete($contact['id']); 104 | $this->assertErrors($response); 105 | } 106 | 107 | public function testDefaultFieldValue() 108 | { 109 | $defaultValue = 'little kitten'; 110 | 111 | $fieldPayload = $this->testPayload; 112 | $fieldPayload['defaultValue'] = $defaultValue; 113 | 114 | // Create the field 115 | $response = $this->api->create($fieldPayload); 116 | $this->assertPayload($response); 117 | $field = $response[$this->api->itemName()]; 118 | 119 | // Create a testing contact 120 | $contactContext = $this->getContext('contacts'); 121 | $response = $contactContext->create(['firstname' => 'Default Value', 'lastname' => 'API test']); 122 | $this->assertErrors($response); 123 | $this->assertTrue(isset($response['contact']['fields']['all'][$field['alias']]), $field['alias'].' does not exist in the field list'); 124 | $this->assertEquals($response['contact']['fields']['all'][$field['alias']], $defaultValue); 125 | $contact = $response['contact']; 126 | 127 | // Clean after youself 128 | $response = $this->api->delete($field['id']); 129 | $this->assertErrors($response); 130 | $response = $contactContext->delete($contact['id']); 131 | $this->assertErrors($response); 132 | } 133 | 134 | public function testEditPut() 135 | { 136 | $this->standardTestEditPut(); 137 | } 138 | 139 | public function testBatchEndpoints() 140 | { 141 | $this->markTestSkipped('Skipped because we\'re waiting for https://github.com/mautic/mautic/issues/9621 to be fixed'); 142 | $this->standardTestBatchEndpoints(); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /tests/Api/DevicesTest.php: -------------------------------------------------------------------------------- 1 | api = $this->getContext('devices'); 21 | $this->testPayload = [ 22 | 'device' => 'desktop', 23 | 'deviceOsName' => 'Ubuntu', 24 | 'deviceOsShortName' => 'UBT', 25 | 'deviceOsPlatform' => 'x64', 26 | ]; 27 | 28 | // Create a contact for test 29 | $contactApi = $this->getContext('contacts'); 30 | $response = $contactApi->create(['firstname' => 'Device API test']); 31 | $this->assertErrors($response); 32 | $this->testPayload['lead'] = $response['contact']['id']; 33 | } 34 | 35 | public function tearDown(): void 36 | { 37 | // Delete a contact from test 38 | $this->api = $this->getContext('contacts'); 39 | $response = $this->api->delete($this->testPayload['lead']); 40 | $this->assertErrors($response); 41 | } 42 | 43 | public function testGetList() 44 | { 45 | $this->standardTestGetList(); 46 | } 47 | 48 | public function testGetListOfSpecificIds() 49 | { 50 | $this->standardTestGetListOfSpecificIds(); 51 | } 52 | 53 | public function testCreateGetAndDelete() 54 | { 55 | $response = $this->api->create($this->testPayload); 56 | $this->assertPayload($response); 57 | 58 | // Test get contact devices endpoint 59 | $contactContext = $this->getContext('contacts'); 60 | $responseDevices = $contactContext->getContactDevices($this->testPayload['lead']); 61 | $this->assertErrors($responseDevices); 62 | $this->assertEquals($responseDevices['total'], 1); 63 | $this->assertFalse(empty($responseDevices['devices'][0]['id'])); 64 | $this->assertEquals($responseDevices['devices'][0]['id'], $response[$this->api->itemName()]['id']); 65 | 66 | $response = $this->api->get($response[$this->api->itemName()]['id']); 67 | $this->assertPayload($response); 68 | 69 | $response = $this->api->delete($response[$this->api->itemName()]['id']); 70 | $this->assertErrors($response); 71 | } 72 | 73 | public function testEditPatch() 74 | { 75 | $editTo = [ 76 | 'deviceOsName' => 'Edubuntu', 77 | ]; 78 | $this->standardTestEditPatch($editTo); 79 | } 80 | 81 | public function testEditPut() 82 | { 83 | $this->standardTestEditPut(); 84 | } 85 | 86 | public function testBatchEndpoints() 87 | { 88 | $this->standardTestBatchEndpoints(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /tests/Api/DynamicContentsTest.php: -------------------------------------------------------------------------------- 1 | api = $this->getContext('dynamicContents'); 19 | $this->testPayload = [ 20 | 'name' => 'test', 21 | 'content' => 'test', 22 | ]; 23 | } 24 | 25 | public function testGetList() 26 | { 27 | $this->standardTestGetList(); 28 | } 29 | 30 | public function testGetListOfSpecificIds() 31 | { 32 | $this->standardTestGetListOfSpecificIds(); 33 | } 34 | 35 | public function testCreateGetAndDelete() 36 | { 37 | $this->standardTestCreateGetAndDelete(); 38 | } 39 | 40 | public function testEditPatch() 41 | { 42 | $editTo = [ 43 | 'name' => 'test2', 44 | ]; 45 | $this->standardTestEditPatch($editTo); 46 | } 47 | 48 | public function testEditPut() 49 | { 50 | $this->standardTestEditPut(); 51 | } 52 | 53 | public function testBatchEndpoints() 54 | { 55 | $this->standardTestBatchEndpoints(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tests/Api/ExceptionsTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($expected, $exception->getMessage(), 'This should return "'.$expected.'"'); 46 | $this->assertEquals(500, $exception->getCode()); 47 | } 48 | 49 | public function testContextNotFoundExceptionCustomMessage() 50 | { 51 | $exception = new ContextNotFoundException(self::CUSTOM_ERROR_MESSAGE); 52 | $this->assertEquals(self::CUSTOM_ERROR_MESSAGE, $exception->getMessage(), 'This should return "'.self::CUSTOM_ERROR_MESSAGE.'"'); 53 | $this->assertEquals(500, $exception->getCode()); 54 | } 55 | 56 | public function testActionNotSupportedException() 57 | { 58 | $expected = 'Action is not supported at this time.'; 59 | $exception = new ActionNotSupportedException(); 60 | $this->assertEquals($expected, $exception->getMessage(), 'This should return "'.$expected.'"'); 61 | $this->assertEquals(500, $exception->getCode()); 62 | } 63 | 64 | public function testActionNotSupportedExceptionCustomMessage() 65 | { 66 | $exception = new ActionNotSupportedException(self::CUSTOM_ERROR_MESSAGE); 67 | $this->assertEquals(self::CUSTOM_ERROR_MESSAGE, $exception->getMessage(), 'This should return "'.self::CUSTOM_ERROR_MESSAGE.'"'); 68 | $this->assertEquals(500, $exception->getCode()); 69 | } 70 | 71 | public function testUnexpectedResponseFormatException() 72 | { 73 | $expected = 'The response returned is in an unexpected format.'."\n\nResponse: "; 74 | $exception = new UnexpectedResponseFormatException(new Response(new HttpResponse())); 75 | $this->assertEquals($expected, $exception->getMessage(), 'This should return "'.$expected.'"'); 76 | $this->assertEquals(500, $exception->getCode()); 77 | } 78 | 79 | public function testUnexpectedResponseFormatExceptionCustomMessage() 80 | { 81 | $expected = self::CUSTOM_ERROR_MESSAGE."\n\nResponse: "; 82 | $exception = new UnexpectedResponseFormatException(new Response(new HttpResponse()), self::CUSTOM_ERROR_MESSAGE); 83 | $this->assertEquals($expected, $exception->getMessage(), 'This should return "'.$expected.'"'); 84 | $this->assertEquals(500, $exception->getCode()); 85 | } 86 | 87 | public function testUnexpectedResponseFormatExceptionCustomCode() 88 | { 89 | $exception = new UnexpectedResponseFormatException(new Response(new HttpResponse()), null, 404); 90 | $this->assertEquals(404, $exception->getCode()); 91 | } 92 | 93 | public function testIncorrectParametersReturnedException() 94 | { 95 | $expected = 'Incorrect parameters returned.'; 96 | $exception = new IncorrectParametersReturnedException(); 97 | $this->assertEquals($expected, $exception->getMessage(), 'This should return "'.$expected.'"'); 98 | $this->assertEquals(500, $exception->getCode()); 99 | } 100 | 101 | public function testIncorrectParametersReturnedExceptionCustomMessage() 102 | { 103 | $exception = new IncorrectParametersReturnedException(self::CUSTOM_ERROR_MESSAGE); 104 | $this->assertEquals(self::CUSTOM_ERROR_MESSAGE, $exception->getMessage(), 'This should return "'.self::CUSTOM_ERROR_MESSAGE.'"'); 105 | $this->assertEquals(500, $exception->getCode()); 106 | } 107 | 108 | public function testRequiredParameterMissingException() 109 | { 110 | $expected = 'Required Parameter is missing.'; 111 | $exception = new RequiredParameterMissingException(); 112 | $this->assertEquals($expected, $exception->getMessage(), 'This should return "'.$expected.'"'); 113 | $this->assertEquals(500, $exception->getCode()); 114 | } 115 | 116 | public function testRequiredParameterMissingExceptionCustomMessage() 117 | { 118 | $exception = new RequiredParameterMissingException(self::CUSTOM_ERROR_MESSAGE); 119 | $this->assertEquals(self::CUSTOM_ERROR_MESSAGE, $exception->getMessage(), 'This should return "'.self::CUSTOM_ERROR_MESSAGE.'"'); 120 | $this->assertEquals(500, $exception->getCode()); 121 | } 122 | 123 | public function testSearchCommands() 124 | { 125 | $this->markTestSkipped('Inherited method but not applicable'); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /tests/Api/FilesTest.php: -------------------------------------------------------------------------------- 1 | api = $this->getContext('files'); 21 | $this->testPayload['file'] = dirname(__DIR__).'/mauticlogo.png'; 22 | 23 | if ('4' == $this->mauticVersion) { 24 | $this->mediaFolder = 'assets'; 25 | } 26 | 27 | $this->assertTrue(file_exists($this->testPayload['file']), 'A file for test at '.$this->testPayload['file'].' does not exist.'); 28 | } 29 | 30 | protected function assertPayload($response, array $payload = [], $isBatch = false, $idColumn = 'id', $callback = null) 31 | { 32 | $this->assertErrors($response); 33 | $this->assertFalse(empty($response[$this->api->itemName()]['name']), 'The '.$this->api->itemName().' file name is empty.'); 34 | } 35 | 36 | public function testGetList() 37 | { 38 | $response = $this->api->getList(); 39 | $this->assertErrors($response); 40 | $this->assertTrue(isset($response[$this->api->listName()])); 41 | } 42 | 43 | public function testGetListSubdir() 44 | { 45 | $this->api->setFolder('images/test_api_dir'); 46 | $createResponse = $this->api->create($this->testPayload); 47 | 48 | $response = $this->api->getList(); 49 | $this->assertTrue(isset($response['files'])); 50 | $this->assertErrors($response); 51 | 52 | $this->api->delete($createResponse['file']['name']); 53 | } 54 | 55 | public function testGetListMediaFiles() 56 | { 57 | $this->api->setFolder($this->mediaFolder); 58 | $response = $this->api->getList(); 59 | $this->assertErrors($response); 60 | } 61 | 62 | public function testCreateAndDeleteImage() 63 | { 64 | $response = $this->api->create($this->testPayload); 65 | $this->assertPayload($response); 66 | $this->assertFalse(empty($response[$this->api->itemName()]['link']), 'The '.$this->api->itemName().' link is empty.'); 67 | 68 | $response = $this->api->delete($response['file']['name']); 69 | $this->assertErrors($response); 70 | $this->assertSuccess($response); 71 | } 72 | 73 | public function testCreateAndDeletePhpScript() 74 | { 75 | // Get this PHP script to send 76 | $this->testPayload['file'] = dirname(__DIR__).'/Api/FilesTest.php'; 77 | $this->assertTrue(file_exists($this->testPayload['file']), 'A file for test at '.$this->testPayload['file'].' does not exist.'); 78 | 79 | $response = $this->api->create($this->testPayload); 80 | $this->assertFalse(empty($response['errors']), 'The PHP script was uploaded! Danger! DANGER!'); 81 | } 82 | 83 | public function testCreateAndDeleteImageInSubdir() 84 | { 85 | $this->api->setFolder('images/test_api_dir'); 86 | $response = $this->api->create($this->testPayload); 87 | $this->assertPayload($response); 88 | $this->assertFalse(empty($response[$this->api->itemName()]['link']), 'The '.$this->api->itemName().' link is empty.'); 89 | 90 | $response = $this->api->delete($response['file']['name']); 91 | $this->assertErrors($response); 92 | $this->assertSuccess($response); 93 | } 94 | 95 | public function testCreateAndDeleteMedia() 96 | { 97 | $this->api->setFolder($this->mediaFolder); 98 | $response = $this->api->create($this->testPayload); 99 | $this->assertPayload($response); 100 | 101 | $response = $this->api->delete($response['file']['name']); 102 | $this->assertErrors($response); 103 | $this->assertSuccess($response); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /tests/Api/FocusTest.php: -------------------------------------------------------------------------------- 1 | api = $this->getContext('focus'); 21 | $this->testPayload = [ 22 | 'name' => 'test', 23 | 'type' => 'notice', 24 | 'website' => 'http://', 25 | 'style' => 'bar', 26 | 'htmlMode' => 1, 27 | 'html' => '
html mode enabled
', 28 | 'properties' => [ 29 | 'bar' => [ 30 | 'allow_hide' => 1, 31 | 'sticky' => 1, 32 | 'size' => 'large', 33 | 'placement' => 'top', 34 | ], 35 | 'modal' => [ 36 | 'placement' => 'top', 37 | ], 38 | 'notification' => [ 39 | 'placement' => 'top_left', 40 | ], 41 | 'animate' => 1, 42 | 'link_activation' => 1, 43 | 'colors' => [ 44 | 'primary' => '27184e', 45 | ], 46 | 'content' => [ 47 | 'headline' => '', 48 | 'font' => 'Arial, Helvetica, sans-serif', 49 | ], 50 | 'when' => 'immediately', 51 | 'frequency' => 'everypage', 52 | 'stop_after_conversion' => 1, 53 | ], 54 | ]; 55 | } 56 | 57 | public function testGetList() 58 | { 59 | $this->standardTestGetList(); 60 | } 61 | 62 | public function testGetListOfSpecificIds() 63 | { 64 | $this->standardTestGetListOfSpecificIds(); 65 | } 66 | 67 | public function testCreateGetAndDelete() 68 | { 69 | $this->standardTestCreateGetAndDelete(); 70 | } 71 | 72 | public function testEditPatch() 73 | { 74 | $editTo = [ 75 | 'name' => 'test2', 76 | ]; 77 | $this->standardTestEditPatch($editTo); 78 | } 79 | 80 | public function testEditPut() 81 | { 82 | $this->standardTestEditPut(); 83 | } 84 | 85 | public function testBatchEndpoints() 86 | { 87 | $this->standardTestBatchEndpoints(); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /tests/Api/MessagesTest.php: -------------------------------------------------------------------------------- 1 | 'emails', 20 | 'sms' => 'smses', 21 | 'notification' => 'notifications', 22 | ]; 23 | 24 | public function setUp(): void 25 | { 26 | $this->api = $this->getContext('messages'); 27 | $this->testPayload = [ 28 | 'name' => 'API message', 29 | 'description' => 'Marketing message created via API unit test', 30 | 'channels' => [ 31 | 'email' => [ 32 | 'channel' => 'email', 33 | 'channelId' => null, 34 | 'isEnabled' => true, 35 | ], 36 | 'sms' => [ 37 | 'channel' => 'sms', 38 | 'channelId' => null, 39 | 'isEnabled' => false, 40 | ], 41 | ], 42 | ]; 43 | } 44 | 45 | protected function setUpPayloadClass() 46 | { 47 | foreach ($this->testPayload['channels'] as $key => $channel) { 48 | $contextName = $this->singularPlural[$channel['channel']]; 49 | $api = $this->getContext($contextName); 50 | $response = $api->create( 51 | [ 52 | 'name' => 'MM API test', // required for all 53 | 'subject' => 'MM API test', // required for email 54 | 'message' => 'test', // required for sms 55 | 'heading' => 'test', // required for notifications 56 | ] 57 | ); 58 | $this->assertErrors($response); 59 | $this->testPayload['channels'][$key]['channelId'] = $response[$api->itemName()]['id']; 60 | } 61 | } 62 | 63 | protected function clearPayloadItems() 64 | { 65 | foreach ($this->testPayload['channels'] as $key => $channel) { 66 | $contextName = $this->singularPlural[$channel['channel']]; 67 | $api = $this->getContext($contextName); 68 | $response = $api->delete($this->testPayload['channels'][$key]['channelId']); 69 | $this->assertErrors($response); 70 | } 71 | } 72 | 73 | protected function assertPayload($response, array $payload = [], $isBatch = false, $idColumn = 'id', $callback = null) 74 | { 75 | parent::assertPayload($response, $payload, $isBatch, $idColumn, $callback); 76 | 77 | // Assert channels 78 | if (!$isBatch) { 79 | $this->assertTrue(!empty($response[$this->api->itemName()])); 80 | foreach ($response[$this->api->itemName()]['channels'] as $responseChannel) { 81 | // All channels will be returned, even the empty ones 82 | if (isset($this->testPayload['channels'][$responseChannel['channel']])) { 83 | unset($responseChannel['id'], $responseChannel['channelName']); 84 | $this->assertSame($this->testPayload['channels'][$responseChannel['channel']], $responseChannel); 85 | } 86 | } 87 | } 88 | } 89 | 90 | public function testGetList() 91 | { 92 | $this->standardTestGetList(); 93 | } 94 | 95 | public function testGetListOfSpecificIds() 96 | { 97 | $this->setUpPayloadClass(); 98 | $this->standardTestGetListOfSpecificIds(); 99 | $this->clearPayloadItems(); 100 | } 101 | 102 | public function testCreateGetAndDelete() 103 | { 104 | $this->setUpPayloadClass(); 105 | $this->standardTestCreateGetAndDelete(); 106 | $this->clearPayloadItems(); 107 | } 108 | 109 | public function testEditPatch() 110 | { 111 | $this->setUpPayloadClass(); 112 | $this->standardTestEditPatch([ 113 | 'name' => 'Modified by PATCH', 114 | ]); 115 | $this->clearPayloadItems(); 116 | } 117 | 118 | public function testEditPut() 119 | { 120 | $this->setUpPayloadClass(); 121 | $this->standardTestEditPut(); 122 | $this->clearPayloadItems(); 123 | } 124 | 125 | public function testBatchEndpoints() 126 | { 127 | $this->standardTestBatchEndpoints(); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /tests/Api/NotesTest.php: -------------------------------------------------------------------------------- 1 | api = $this->getContext('notes'); 21 | $this->testPayload = [ 22 | 'text' => 'Contact note created via API request', 23 | 'type' => 'general', 24 | ]; 25 | 26 | // Create a contact for test 27 | $contactApi = $this->getContext('contacts'); 28 | $response = $contactApi->create(['firstname' => 'Note API test']); 29 | $this->assertErrors($response); 30 | $this->testPayload['lead'] = $response['contact']['id']; 31 | } 32 | 33 | public function tearDown(): void 34 | { 35 | // Delete a contact from test 36 | $this->api = $this->getContext('contacts'); 37 | $response = $this->api->delete($this->testPayload['lead']); 38 | $this->assertErrors($response); 39 | } 40 | 41 | public function testGetList() 42 | { 43 | $this->standardTestGetList(); 44 | } 45 | 46 | public function testGetListOfSpecificIds() 47 | { 48 | $this->standardTestGetListOfSpecificIds(); 49 | } 50 | 51 | public function testCreateGetAndDelete() 52 | { 53 | $response = $this->api->create($this->testPayload); 54 | $this->assertPayload($response); 55 | 56 | // Test get contact notes endpoint 57 | $contactContext = $this->getContext('contacts'); 58 | $responseNotes = $contactContext->getContactNotes($this->testPayload['lead']); 59 | $this->assertErrors($responseNotes); 60 | $this->assertEquals($responseNotes['total'], 1); 61 | $this->assertFalse(empty($responseNotes['notes'])); 62 | 63 | $response = $this->api->get($response[$this->api->itemName()]['id']); 64 | $this->assertPayload($response); 65 | 66 | $response = $this->api->delete($response[$this->api->itemName()]['id']); 67 | $this->assertErrors($response); 68 | } 69 | 70 | public function testEditPatch() 71 | { 72 | $editTo = [ 73 | 'text' => 'test2', 74 | ]; 75 | $this->standardTestEditPatch($editTo); 76 | } 77 | 78 | public function testEditPut() 79 | { 80 | $this->standardTestEditPut(); 81 | } 82 | 83 | public function testBatchEndpoints() 84 | { 85 | $this->standardTestBatchEndpoints(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /tests/Api/NotificationsTest.php: -------------------------------------------------------------------------------- 1 | api = $this->getContext('notifications'); 19 | $this->testPayload = [ 20 | 'name' => 'test', 21 | 'heading' => 'API test heading', 22 | 'message' => 'API test message', 23 | ]; 24 | } 25 | 26 | public function testGetList() 27 | { 28 | $this->standardTestGetList(); 29 | } 30 | 31 | public function testGetListOfSpecificIds() 32 | { 33 | $this->standardTestGetListOfSpecificIds(); 34 | } 35 | 36 | public function testCreateGetAndDelete() 37 | { 38 | $this->standardTestCreateGetAndDelete(); 39 | } 40 | 41 | public function testEditPatch() 42 | { 43 | $editTo = [ 44 | 'name' => 'test2', 45 | ]; 46 | $this->standardTestEditPatch($editTo); 47 | } 48 | 49 | public function testEditPut() 50 | { 51 | $this->standardTestEditPut(); 52 | } 53 | 54 | public function testBatchEndpoints() 55 | { 56 | $this->standardTestBatchEndpoints(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests/Api/PagesTest.php: -------------------------------------------------------------------------------- 1 | api = $this->getContext('pages'); 19 | $this->testPayload = [ 20 | 'title' => 'test', 21 | 'template' => 'blank', 22 | 'customHtml' => ' 23 | 24 | 25 | 26 | 27 | 28 |
29 |
30 | 31 | 32 | 37 | 38 |
33 |
34 | Awesome Co 35 |
36 |
39 |
40 |
41 | 42 | ', 43 | ]; 44 | } 45 | 46 | public function testGetList() 47 | { 48 | $this->standardTestGetList(); 49 | } 50 | 51 | public function testGetListOfSpecificIds() 52 | { 53 | $this->standardTestGetListOfSpecificIds(); 54 | } 55 | 56 | public function testCreateGetAndDelete() 57 | { 58 | $this->standardTestCreateGetAndDelete(); 59 | } 60 | 61 | public function testEditPatch() 62 | { 63 | $editTo = [ 64 | 'title' => 'test2', 65 | ]; 66 | $this->standardTestEditPatch($editTo); 67 | } 68 | 69 | public function testEditPut() 70 | { 71 | $this->standardTestEditPut(); 72 | } 73 | 74 | public function testBatchEndpoints() 75 | { 76 | $this->standardTestBatchEndpoints(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /tests/Api/PointGroupsTest.php: -------------------------------------------------------------------------------- 1 | api = $this->getContext('pointGroups'); 12 | $this->testPayload = [ 13 | 'name' => 'New Point Group', 14 | 'description' => 'Description of the new point group', 15 | ]; 16 | } 17 | 18 | public function testGetList(): void 19 | { 20 | $this->standardTestGetList(); 21 | } 22 | 23 | public function testGetListOfSpecificIds(): void 24 | { 25 | $this->standardTestGetListOfSpecificIds(); 26 | } 27 | 28 | public function testCreateGetAndDelete(): void 29 | { 30 | $this->standardTestCreateGetAndDelete(); 31 | } 32 | 33 | public function testEditPatch(): void 34 | { 35 | $editTo = [ 36 | 'name' => 'Updated Point Group Name', 37 | ]; 38 | $this->standardTestEditPatch($editTo); 39 | } 40 | 41 | public function testEditPut(): void 42 | { 43 | $this->standardTestEditPut(); 44 | } 45 | 46 | public function testBatchEndpoints(): void 47 | { 48 | $this->standardTestBatchEndpoints(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/Api/PointTriggersTest.php: -------------------------------------------------------------------------------- 1 | api = $this->getContext('pointTriggers'); 19 | $this->testPayload = [ 20 | 'name' => 'test', 21 | 'description' => 'created as a API test', 22 | 'points' => 5, 23 | 'color' => '4e5d9d', 24 | 'triggerExistingLeads' => false, 25 | 'events' => [ 26 | [ 27 | 'name' => 'tag test event', 28 | 'description' => 'created as a API test', 29 | 'type' => 'lead.changetags', 30 | 'order' => 1, 31 | 'properties' => [ 32 | 'add_tags' => ['tag-a'], 33 | 'remove_tags' => [], 34 | ], 35 | ], 36 | [ 37 | 'name' => 'send email test event', 38 | 'description' => 'created as a API test', 39 | 'type' => 'email.send', 40 | 'order' => 2, 41 | 'properties' => [ 42 | 'email' => 1, 43 | ], 44 | ], 45 | ], 46 | ]; 47 | } 48 | 49 | protected function assertPayload($response, array $payload = [], $isBatch = false, $idColumn = 'id', $callback = null) 50 | { 51 | parent::assertPayload($response, $payload, $isBatch, $idColumn, [$this, 'validateComponentsPayload']); 52 | } 53 | 54 | protected function validateComponentsPayload($itemProp, $itemVal, $item) 55 | { 56 | $this->assertFalse(empty($item['events']), 'The point trigger event array is empty: '.var_export($item, true)); 57 | 58 | switch ($itemProp) { 59 | case 'events': 60 | end($itemVal); 61 | $key = key($itemVal); 62 | $this->assertSame($itemVal[$key]['name'], $item['events'][$key]['name'], 'Event names do not match: '.var_export($item, true)); 63 | break; 64 | default: 65 | $this->assertSame($item[$itemProp], $itemVal); 66 | } 67 | } 68 | 69 | public function testGetList() 70 | { 71 | $this->standardTestGetList(); 72 | } 73 | 74 | public function testGetListOfSpecificIds() 75 | { 76 | $this->standardTestGetListOfSpecificIds(); 77 | } 78 | 79 | public function testCreateGetAndDelete() 80 | { 81 | $this->standardTestCreateGetAndDelete(); 82 | } 83 | 84 | public function testEditPatch() 85 | { 86 | $editTo = [ 87 | 'name' => 'test2', 88 | 'events' => $this->testPayload['events'], 89 | ]; 90 | $this->standardTestEditPatch($editTo); 91 | } 92 | 93 | public function testEditPut() 94 | { 95 | $this->standardTestEditPut(); 96 | } 97 | 98 | public function testEventDeleteViaPut() 99 | { 100 | $response = $this->api->edit(10000, $this->testPayload, true); 101 | $this->assertErrors($response); 102 | 103 | // Remove the trigger events 104 | unset($response[$this->api->itemName()]['events']); 105 | 106 | // Edit the same entitiy without the fields and actions 107 | $response = $this->api->edit( 108 | $response[$this->api->itemName()]['id'], 109 | $response[$this->api->itemName()], 110 | true 111 | ); 112 | 113 | $this->assertErrors($response); 114 | $this->assertTrue(empty($response[$this->api->itemName()]['events']), 'Trigger events were not deleted via PUT request'); 115 | 116 | // now delete the form 117 | $response = $this->api->delete($response[$this->api->itemName()]['id']); 118 | $this->assertErrors($response); 119 | } 120 | 121 | public function testDeleteEvents() 122 | { 123 | $eventIds = []; 124 | $response = $this->api->create($this->testPayload); 125 | $this->assertErrors($response); 126 | 127 | foreach ($response[$this->api->itemName()]['events'] as $event) { 128 | $eventIds[] = $event['id']; 129 | } 130 | 131 | $response = $this->api->deleteTriggerEvents($response[$this->api->itemName()]['id'], $eventIds); 132 | $this->assertErrors($response); 133 | $this->assertTrue(empty($response[$this->api->itemName()]['events']), 'Events were not deleted'); 134 | 135 | // now delete the trigger 136 | $response = $this->api->delete($response[$this->api->itemName()]['id']); 137 | $this->assertErrors($response); 138 | } 139 | 140 | public function testGetEventTypes() 141 | { 142 | $response = $this->api->getEventTypes(); 143 | $this->assertErrors($response); 144 | $this->assertFalse(empty($response['eventTypes']), 'The eventTypes array is empty.'); 145 | } 146 | 147 | public function testBatchEndpoints() 148 | { 149 | $this->standardTestBatchEndpoints(null, function ($response, &$batch, $action) { 150 | switch ($action) { 151 | case 'create': 152 | foreach ($batch as &$item) { 153 | unset($item['events']); 154 | } 155 | break; 156 | } 157 | }); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /tests/Api/PointsTest.php: -------------------------------------------------------------------------------- 1 | api = $this->getContext('points'); 19 | $this->testPayload = [ 20 | 'name' => 'test', 21 | 'delta' => 5, 22 | 'type' => 'page.hit', 23 | 'description' => 'created as a API test', 24 | ]; 25 | } 26 | 27 | public function testGetList() 28 | { 29 | $this->standardTestGetList(); 30 | } 31 | 32 | public function testGetListOfSpecificIds() 33 | { 34 | $this->standardTestGetListOfSpecificIds(); 35 | } 36 | 37 | public function testCreateGetAndDelete() 38 | { 39 | $this->standardTestCreateGetAndDelete(); 40 | } 41 | 42 | public function testEditPatch() 43 | { 44 | $editTo = [ 45 | 'name' => 'test2', 46 | ]; 47 | $this->standardTestEditPatch($editTo); 48 | } 49 | 50 | public function testEditPut() 51 | { 52 | $this->standardTestEditPut(); 53 | } 54 | 55 | public function testGetPointActionTypes() 56 | { 57 | $response = $this->api->getPointActionTypes(); 58 | $this->assertErrors($response); 59 | $this->assertFalse(empty($response['pointActionTypes']), 'The pointActionTypes array is empty.'); 60 | } 61 | 62 | public function testBatchEndpoints() 63 | { 64 | $this->standardTestBatchEndpoints(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tests/Api/ReportsTest.php: -------------------------------------------------------------------------------- 1 | api = $this->getContext('reports'); 19 | } 20 | 21 | public function testGet() 22 | { 23 | $response = $this->api->get(1); 24 | 25 | $this->assertErrors($response); 26 | $this->assertGreaterThanOrEqual(0, $response['totalResults']); 27 | $this->assertGreaterThanOrEqual(0, count($response['dataColumns'])); 28 | $this->assertSame(0, $response['limit']); 29 | $this->assertSame(1, $response['page']); 30 | } 31 | 32 | public function testGetCustom() 33 | { 34 | $limit = 5; 35 | $page = 2; 36 | $dateFrom = new \DateTime('1 year ago', new \DateTimeZone('UTC')); 37 | $dateTo = new \DateTime('now', new \DateTimeZone('UTC')); 38 | $response = $this->api->get(1, $limit, $page, $dateFrom, $dateTo); 39 | 40 | $this->assertErrors($response); 41 | $this->assertGreaterThanOrEqual(0, $response['totalResults']); 42 | $this->assertGreaterThanOrEqual(0, count($response['dataColumns'])); 43 | $this->assertSame($limit, $response['limit']); 44 | $this->assertSame($page, $response['page']); 45 | // Mautic will modify the times slightly so check only for date. 46 | $this->assertSame($dateFrom->format('Y-m-d'), (new \DateTime($response['dateFrom']))->format('Y-m-d'), 'DateFrom does not match'); 47 | $this->assertSame($dateTo->format('Y-m-d'), (new \DateTime($response['dateTo']))->format('Y-m-d'), 'DateTo does not match'); 48 | } 49 | 50 | public function testGetList() 51 | { 52 | $this->standardTestGetList(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tests/Api/ResponseInfoTest.php: -------------------------------------------------------------------------------- 1 | api = $this->getContext('contacts'); 19 | $response = $this->api->getList('', 0, 1); 20 | $this->assertErrors($response); 21 | } 22 | 23 | public function testGetVersion() 24 | { 25 | $version = $this->api->getMauticVersion(); 26 | $this->assertMatchesRegularExpression("/^(\d+\.)?(\d+\.)?(.+|\d+)$/", $version); 27 | } 28 | 29 | public function testResponseHeaders() 30 | { 31 | $headers = $this->api->getResponseHeaders(); 32 | $this->assertEquals($headers['Content-Type'], 'application/json'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/Api/RolesTest.php: -------------------------------------------------------------------------------- 1 | api = $this->getContext('roles'); 19 | $this->testPayload = [ 20 | 'name' => 'API test role', 21 | 'description' => 'created via AIP', 22 | 'rawPermissions' => [ 23 | 'email:emails' => [ 24 | 'viewown', 25 | 'viewother', 26 | ], 27 | ], 28 | ]; 29 | } 30 | 31 | public function testGetList() 32 | { 33 | $this->standardTestGetList(); 34 | } 35 | 36 | public function testGetListOfSpecificIds() 37 | { 38 | $this->standardTestGetListOfSpecificIds(); 39 | } 40 | 41 | public function testCreateGetAndDelete() 42 | { 43 | $this->standardTestCreateGetAndDelete(); 44 | } 45 | 46 | public function testEditPatch() 47 | { 48 | $editTo = [ 49 | 'name' => 'test2', 50 | ]; 51 | $this->standardTestEditPatch($editTo); 52 | } 53 | 54 | public function testEditPut() 55 | { 56 | $this->standardTestEditPut(); 57 | } 58 | 59 | public function testBatchEndpoints() 60 | { 61 | $this->standardTestBatchEndpoints(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tests/Api/SegmentsTest.php: -------------------------------------------------------------------------------- 1 | api = $this->getContext('segments'); 26 | $this->testPayload = [ 27 | 'name' => 'List all GMail contacts', 28 | 'description' => 'Created via API Library unit tests', 29 | 'isGlobal' => true, 30 | 'filters' => [ 31 | [ 32 | 'glue' => 'and', 33 | 'field' => 'email', 34 | 'object' => 'lead', 35 | 'type' => 'email', 36 | 'properties' => [ 37 | 'filter' => '*@gmail.com', 38 | ], 39 | 'operator' => 'like', 40 | ], 41 | ], 42 | ]; 43 | } 44 | 45 | public function testGetList() 46 | { 47 | $response = $this->api->getList(); 48 | $this->assertErrors($response); 49 | } 50 | 51 | public function testGetListOfSpecificIds() 52 | { 53 | // Create some items first 54 | $itemIds = []; 55 | $response = $this->api->create($this->testPayload); 56 | $this->assertErrors($response); 57 | $itemIds[] = $response[$this->api->itemName()]['id']; 58 | $response = $this->api->create($this->testPayload); 59 | $this->assertErrors($response); 60 | $itemIds[] = $response[$this->api->itemName()]['id']; 61 | 62 | $search = 'ids:'.implode(',', $itemIds); 63 | 64 | $response = $this->api->getList($search); 65 | $this->assertErrors($response); 66 | $this->assertEquals(count($itemIds), $response['total']); 67 | 68 | foreach ($response['lists'] as $item) { 69 | $this->assertTrue(in_array($item['id'], $itemIds)); 70 | $this->api->delete($item['id']); 71 | $this->assertErrors($response); 72 | } 73 | } 74 | 75 | public function testGetListMinimal() 76 | { 77 | $response = $this->api->getList('', 0, 0, '', 'ASC', false, true); 78 | $this->assertErrors($response); 79 | } 80 | 81 | public function testCreateGetAndDelete() 82 | { 83 | // Test Create 84 | $response = $this->api->create($this->testPayload); 85 | $this->assertPayload($response); 86 | 87 | // Test Get 88 | $response = $this->api->get($response[$this->api->itemName()]['id']); 89 | $this->assertPayload($response); 90 | 91 | // Test Delete 92 | $response = $this->api->delete($response[$this->api->itemName()]['id']); 93 | $this->assertErrors($response); 94 | } 95 | 96 | public function testEditPatch() 97 | { 98 | $response = $this->api->edit(10000, $this->testPayload); 99 | 100 | // there should be an error as the segment shouldn't exist 101 | $this->assertTrue(isset($response['errors'][0]), $response['errors'][0]['message']); 102 | 103 | $response = $this->api->create($this->testPayload); 104 | $this->assertPayload($response); 105 | 106 | $update = [ 107 | 'name' => 'test2', 108 | ]; 109 | 110 | $response = $this->api->edit($response[$this->api->itemName()]['id'], $update); 111 | $this->assertPayload($response, $update); 112 | 113 | // now delete the segment 114 | $response = $this->api->delete($response[$this->api->itemName()]['id']); 115 | $this->assertErrors($response); 116 | } 117 | 118 | public function testEditPut() 119 | { 120 | $response = $this->api->edit(10000, $this->testPayload, true); 121 | $this->assertPayload($response); 122 | 123 | // now delete the segment 124 | $response = $this->api->delete($response[$this->api->itemName()]['id']); 125 | $this->assertErrors($response); 126 | } 127 | 128 | public function testAddAndRemove() 129 | { 130 | // Create contact 131 | $contactApi = $this->getContext('contacts'); 132 | $response = $contactApi->create(['firstname' => 'API segments test']); 133 | $this->assertErrors($response); 134 | $contact = $response['contact']; 135 | 136 | // Create segment 137 | $response = $this->api->create($this->testPayload); 138 | $this->assertPayload($response); 139 | $segment = $response[$this->api->itemName()]; 140 | 141 | // Add the contact to the segment 142 | $response = $this->api->addContact($segment['id'], $contact['id']); 143 | $this->assertErrors($response); 144 | 145 | // Test get contact segments API endpoint 146 | $response = $contactApi->getContactSegments($contact['id']); 147 | $this->assertErrors($response); 148 | $this->assertEquals($response['total'], 1); 149 | $this->assertFalse(empty($response['lists'])); 150 | 151 | // Remove the contact from the segment 152 | $response = $this->api->removeContact($segment['id'], $contact['id']); 153 | $this->assertErrors($response); 154 | 155 | // Delete the contact and the segment 156 | $response = $contactApi->delete($contact['id']); 157 | $this->assertErrors($response); 158 | $response = $this->api->delete($segment['id']); 159 | $this->assertErrors($response); 160 | } 161 | 162 | public function testBatchEndpoints() 163 | { 164 | $this->standardTestBatchEndpoints(null, function ($response, &$batch, $action) { 165 | switch ($action) { 166 | // Add extra values to the batch after create, as the API returns them in the response. 167 | // This is probably related to https://github.com/mautic/mautic/pull/8649 168 | case 'create': 169 | foreach ($batch as &$item) { 170 | $item['filters'][0] += ['filter' => '*@gmail.com', 'display' => null]; 171 | } 172 | break; 173 | } 174 | }); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /tests/Api/SmsesTest.php: -------------------------------------------------------------------------------- 1 | api = $this->getContext('smses'); 19 | $this->testPayload = [ 20 | 'name' => 'test', 21 | 'message' => 'API test message', 22 | ]; 23 | } 24 | 25 | public function testGetList() 26 | { 27 | $this->standardTestGetList(); 28 | } 29 | 30 | public function testGetListOfSpecificIds() 31 | { 32 | $this->standardTestGetListOfSpecificIds(); 33 | } 34 | 35 | public function testCreateGetAndDelete() 36 | { 37 | $this->standardTestCreateGetAndDelete(); 38 | } 39 | 40 | public function testEditPatch() 41 | { 42 | $editTo = [ 43 | 'name' => 'test2', 44 | ]; 45 | $this->standardTestEditPatch($editTo); 46 | } 47 | 48 | public function testEditPut() 49 | { 50 | $this->standardTestEditPut(); 51 | } 52 | 53 | public function testBatchEndpoints() 54 | { 55 | $this->standardTestBatchEndpoints(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tests/Api/StagesTest.php: -------------------------------------------------------------------------------- 1 | api = $this->getContext('stages'); 19 | $this->testPayload = [ 20 | 'name' => 'test', 21 | ]; 22 | } 23 | 24 | public function testGetList() 25 | { 26 | $this->standardTestGetList(); 27 | } 28 | 29 | public function testGetListOfSpecificIds() 30 | { 31 | $this->standardTestGetListOfSpecificIds(); 32 | } 33 | 34 | public function testCreateGetAndDelete() 35 | { 36 | $this->standardTestCreateGetAndDelete(); 37 | } 38 | 39 | public function testEditPatch() 40 | { 41 | $editTo = [ 42 | 'name' => 'test2', 43 | ]; 44 | $this->standardTestEditPatch($editTo); 45 | } 46 | 47 | public function testEditPut() 48 | { 49 | $this->standardTestEditPut(); 50 | } 51 | 52 | public function testAddAndRemove() 53 | { 54 | // Create contact 55 | $contactsContext = $this->getContext('contacts'); 56 | $response = $contactsContext->create(['firstname' => 'API stages test']); 57 | $this->assertErrors($response); 58 | $contact = $response['contact']; 59 | 60 | // Create stage 61 | $response = $this->api->create($this->testPayload); 62 | $this->assertPayload($response); 63 | $stage = $response[$this->api->itemName()]; 64 | 65 | // Add contact to the stage 66 | $response = $this->api->addContact($stage['id'], $contact['id']); 67 | $this->assertErrors($response); 68 | $this->assertSuccess($response); 69 | 70 | // Remove the contact from the stage 71 | $response = $this->api->removeContact($stage['id'], $contact['id']); 72 | $this->assertErrors($response); 73 | $this->assertSuccess($response); 74 | 75 | // Delete the contact and the stage 76 | $response = $contactsContext->delete($contact['id']); 77 | $this->assertErrors($response); 78 | $response = $this->api->delete($stage['id']); 79 | $this->assertErrors($response); 80 | } 81 | 82 | public function testBatchEndpoints() 83 | { 84 | $this->standardTestBatchEndpoints(); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /tests/Api/TagsTest.php: -------------------------------------------------------------------------------- 1 | api = $this->getContext('tags'); 19 | $this->testPayload = [ 20 | 'tag' => 'API-tag', 21 | ]; 22 | } 23 | 24 | public function testGetList() 25 | { 26 | $this->standardTestGetList(); 27 | } 28 | 29 | public function testGetListOfSpecificIds() 30 | { 31 | // Create some tags first 32 | $itemIds = []; 33 | for ($i = 0; $i <= 2; ++$i) { 34 | $response = $this->api->create(['tag' => 'api-test-tag'.$i]); 35 | $this->assertErrors($response); 36 | $itemIds[] = $response[$this->api->itemName()]['id']; 37 | } 38 | 39 | $search = 'ids:'.implode(',', $itemIds); 40 | $response = $this->api->getList($search); 41 | $this->assertErrors($response); 42 | $this->assertEquals(count($itemIds), $response['total']); 43 | 44 | foreach ($response[$this->api->listName()] as $item) { 45 | $this->assertTrue(in_array($item['id'], $itemIds)); 46 | $this->api->delete($item['id']); 47 | $this->assertErrors($response); 48 | } 49 | } 50 | 51 | public function testCreateGetAndDelete() 52 | { 53 | $this->standardTestCreateGetAndDelete(); 54 | } 55 | 56 | public function testEditPatch() 57 | { 58 | $editTo = [ 59 | 'tag' => 'API-tag-edit', 60 | ]; 61 | $this->standardTestEditPatch($editTo); 62 | } 63 | 64 | public function testEditPut() 65 | { 66 | $this->standardTestEditPut(); 67 | } 68 | 69 | public function testBatchEndpoints() 70 | { 71 | $this->standardTestBatchEndpoints( 72 | [ 73 | ['tag' => 'api-test-tag1'], 74 | ['tag' => 'api-test-tag2'], 75 | ['tag' => 'api-test-tag3'], 76 | ] 77 | ); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /tests/Api/ThemesTest.php: -------------------------------------------------------------------------------- 1 | api = $this->getContext('themes'); 19 | } 20 | 21 | public function testGetList() 22 | { 23 | $response = $this->api->getList(); 24 | $this->assertErrors($response); 25 | $this->assertTrue(isset($response[$this->api->listName()])); 26 | $this->assertTrue(isset($response[$this->api->listName()]['blank'])); 27 | $this->assertTrue(isset($response[$this->api->listName()]['blank']['config'])); 28 | $this->assertSame($response[$this->api->listName()]['blank']['name'], 'Blank'); 29 | $this->assertSame($response[$this->api->listName()]['blank']['key'], 'blank'); 30 | } 31 | 32 | public function testGetCreateAndDelete() 33 | { 34 | $themeName = 'api_test'; 35 | 36 | // Get blank theme 37 | $response = $this->api->get('blank'); 38 | $this->assertErrors($response); 39 | $this->assertFalse(empty($response['file'])); 40 | $this->assertTrue(file_exists($response['file'])); 41 | 42 | // Test setTemporaryFilePath 43 | $dir = sys_get_temp_dir().DIRECTORY_SEPARATOR.'mytempdir'; 44 | $this->api->setTemporaryFilePath($dir); 45 | $response = $this->api->get('blank'); 46 | $this->assertErrors($response); 47 | $this->assertFalse(empty($response['file'])); 48 | $this->assertTrue(file_exists($response['file'])); 49 | $this->assertEquals(realpath($dir), dirname($response['file'])); 50 | 51 | // Create a new theme from the theme we just got 52 | $tmpFile = dirname(__DIR__).'/'.$themeName.'.zip'; 53 | $this->assertTrue(rename($response['file'], $tmpFile), 'could not create '.$tmpFile); 54 | $response = $this->api->create(['file' => $tmpFile]); 55 | $this->assertErrors($response); 56 | $this->assertTrue($response['success']); 57 | 58 | // Delete the local copy of the theme 59 | unlink($tmpFile); 60 | 61 | // Ensure the theme we created is listed 62 | $response = $this->api->getList(); 63 | $this->assertErrors($response); 64 | $this->assertTrue(isset($response[$this->api->listName()][$themeName])); 65 | 66 | // Delete the theme we just created 67 | $response = $this->api->delete($themeName); 68 | $this->assertErrors($response); 69 | $this->assertSuccess($response); 70 | 71 | // Ensure the theme we created is not listed 72 | $response = $this->api->getList(); 73 | $this->assertErrors($response); 74 | $this->assertFalse(isset($response[$this->api->listName()][$themeName])); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /tests/Api/TweetsTest.php: -------------------------------------------------------------------------------- 1 | api = $this->getContext('tweets'); 19 | $this->testPayload = [ 20 | 'name' => 'API test', 21 | 'text' => 'API test tweet', 22 | 'description' => 'Created via API tests', 23 | ]; 24 | } 25 | 26 | public function testGetList() 27 | { 28 | $this->standardTestGetList(); 29 | } 30 | 31 | public function testGetListOfSpecificIds() 32 | { 33 | $this->standardTestGetListOfSpecificIds(); 34 | } 35 | 36 | public function testCreateGetAndDelete() 37 | { 38 | $this->standardTestCreateGetAndDelete(); 39 | } 40 | 41 | public function testEditPatch() 42 | { 43 | $editTo = [ 44 | 'name' => 'API test name updated', 45 | ]; 46 | $this->standardTestEditPatch($editTo); 47 | } 48 | 49 | public function testEditPut() 50 | { 51 | $this->standardTestEditPut(); 52 | } 53 | 54 | public function testBatchEndpoints() 55 | { 56 | $this->standardTestBatchEndpoints(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests/Api/UsersTest.php: -------------------------------------------------------------------------------- 1 | api = $this->getContext('users'); 21 | $this->testPayload = $this->getUniqueUser(); 22 | } 23 | 24 | protected function generateRandomUsername($length = 8) 25 | { 26 | $x = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; 27 | 28 | return 'API_'.substr(str_shuffle(str_repeat($x, ceil($length / strlen($x)))), 1, $length); 29 | } 30 | 31 | protected function getUniqueUser() 32 | { 33 | $username = $this->generateRandomUsername(); 34 | 35 | return [ 36 | 'username' => $username, 37 | 'firstName' => 'API', 38 | 'lastName' => 'Test', 39 | 'email' => $username.'@email.com', 40 | 'plainPassword' => [ 41 | 'password' => 'topSecret007', 42 | 'confirm' => 'topSecret007', 43 | ], 44 | 'role' => 1, // Should exist in every Mautic instance 45 | ]; 46 | } 47 | 48 | public function testGetList() 49 | { 50 | $this->standardTestGetList(); 51 | } 52 | 53 | public function testGetListOfSpecificIds() 54 | { 55 | // Create some items first 56 | $itemIds = []; 57 | $response = $this->api->create($this->testPayload); 58 | $this->assertErrors($response); 59 | $itemIds[] = $response[$this->api->itemName()]['id']; 60 | $user2 = $this->getUniqueUser(); 61 | $response = $this->api->create($user2); 62 | $this->assertErrors($response); 63 | $itemIds[] = $response[$this->api->itemName()]['id']; 64 | 65 | $search = 'ids:'.implode(',', $itemIds); 66 | $response = $this->api->getList($search); 67 | $this->assertErrors($response); 68 | $this->assertEquals(count($itemIds), $response['total']); 69 | 70 | foreach ($response[$this->api->listName()] as $item) { 71 | $this->assertTrue(in_array($item['id'], $itemIds)); 72 | $this->api->delete($item['id']); 73 | $this->assertErrors($response); 74 | } 75 | } 76 | 77 | public function testCreateGetAndDelete() 78 | { 79 | $this->standardTestCreateGetAndDelete(); 80 | } 81 | 82 | public function testEditPatch() 83 | { 84 | $editTo = [ 85 | 'lastName' => 'test2', 86 | ]; 87 | $this->standardTestEditPatch($editTo); 88 | } 89 | 90 | public function testEditPut() 91 | { 92 | $this->standardTestEditPut(); 93 | } 94 | 95 | public function testGetSelf() 96 | { 97 | $response = $this->api->getSelf(); 98 | $this->assertErrors($response); 99 | } 100 | 101 | public function testGetSelfPermissionsString() 102 | { 103 | $response = $this->api->getSelf(); 104 | $this->assertErrors($response); 105 | 106 | $permission = 'user:users:create'; 107 | $response = $this->api->checkPermission($response['id'], $permission); 108 | $this->assertErrors($response); 109 | $this->assertTrue(isset($response[$permission])); 110 | } 111 | 112 | public function testGetSelfPermissionsArray() 113 | { 114 | $response = $this->api->getSelf(); 115 | $this->assertErrors($response); 116 | 117 | $permission = ['user:users:create', 'user:users:edit']; 118 | $response = $this->api->checkPermission($response['id'], $permission); 119 | $this->assertErrors($response); 120 | foreach ($permission as $p) { 121 | $this->assertTrue(isset($response[$p])); 122 | } 123 | } 124 | 125 | public function testBatchEndpoints() 126 | { 127 | $this->markTestSkipped('Skipped because we\'re waiting for https://github.com/mautic/mautic/issues/9621 to be fixed'); 128 | 129 | $batch = [ 130 | $this->getUniqueUser(), 131 | $this->getUniqueUser(), 132 | $this->getUniqueUser(), 133 | ]; 134 | 135 | $this->standardTestBatchEndpoints($batch); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /tests/Api/WebhooksTest.php: -------------------------------------------------------------------------------- 1 | api = $this->getContext('webhooks'); 21 | $this->testPayload = [ 22 | 'name' => 'test', 23 | 'description' => 'Created via API', 24 | 'webhookUrl' => 'http://some.url', 25 | 'eventsOrderbyDir' => 'DESC', 26 | 'triggers' => [ 27 | 'mautic.lead_post_save_update', 28 | 'mautic.lead_post_save_new', 29 | ], 30 | ]; 31 | } 32 | 33 | public function testGetList() 34 | { 35 | $this->standardTestGetList(); 36 | } 37 | 38 | public function testGetListOfSpecificIds() 39 | { 40 | $this->standardTestGetListOfSpecificIds(); 41 | } 42 | 43 | public function testCreateGetAndDelete() 44 | { 45 | $this->standardTestCreateGetAndDelete(); 46 | } 47 | 48 | public function testCreateWithWrongOrderDir() 49 | { 50 | $this->testPayload['eventsOrderbyDir'] = 'abrakadabra'; 51 | $response = $this->api->create($this->testPayload); 52 | 53 | $this->assertFalse(empty($response['errors'])); 54 | } 55 | 56 | public function testCreateWithUndefinedOrderDir() 57 | { 58 | unset($this->testPayload['eventsOrderbyDir']); 59 | 60 | $this->standardTestCreateGetAndDelete($this->testPayload); 61 | } 62 | 63 | public function testEditPatch() 64 | { 65 | $editTo = [ 66 | 'name' => 'test2', 67 | 'description' => 'Updated via API', 68 | ]; 69 | $this->standardTestEditPatch($editTo); 70 | } 71 | 72 | public function testEditPut() 73 | { 74 | $this->standardTestEditPut(); 75 | } 76 | 77 | public function testBatchEndpoints() 78 | { 79 | $this->standardTestBatchEndpoints(); 80 | } 81 | 82 | public function testAddingTriggerToWebhook() 83 | { 84 | $response = $this->api->create($this->testPayload); 85 | $this->assertPayload($response); 86 | 87 | $editTo = $response[$this->api->itemName()]; 88 | 89 | $editTo['triggers'][] = 'mautic.email_on_open'; 90 | 91 | $response = $this->api->edit($response[$this->api->itemName()]['id'], $editTo); 92 | $this->assertPayload($response, $editTo); 93 | 94 | $response = $this->api->delete($response[$this->api->itemName()]['id']); 95 | $this->assertErrors($response); 96 | } 97 | 98 | public function testRemovingTriggerFromWebhook() 99 | { 100 | $response = $this->api->create($this->testPayload); 101 | $this->assertPayload($response); 102 | 103 | $editTo = $response[$this->api->itemName()]; 104 | 105 | $editTo['triggers'] = ['mautic.email_on_open']; 106 | 107 | $response = $this->api->edit($response[$this->api->itemName()]['id'], $editTo, true); 108 | $this->assertPayload($response, $editTo); 109 | 110 | $response = $this->api->delete($response[$this->api->itemName()]['id']); 111 | $this->assertErrors($response); 112 | } 113 | 114 | public function testGetWebhookTriggers() 115 | { 116 | $response = $this->api->getTriggers(); 117 | 118 | $this->assertTrue(isset($response['triggers'])); 119 | 120 | $this->assertTrue(isset($response['triggers']['mautic.lead_post_save_new'])); 121 | $this->assertTrue(isset($response['triggers']['mautic.lead_post_save_update'])); 122 | $this->assertTrue(isset($response['triggers']['mautic.lead_post_delete'])); 123 | $this->assertTrue(isset($response['triggers']['mautic.lead_points_change'])); 124 | $this->assertTrue(isset($response['triggers']['mautic.email_on_open'])); 125 | $this->assertTrue(isset($response['triggers']['mautic.form_on_submit'])); 126 | $this->assertTrue(isset($response['triggers']['mautic.page_on_hit'])); 127 | 128 | $this->assertTrue(isset($response['triggers']['mautic.page_on_hit']['label'])); 129 | $this->assertTrue(isset($response['triggers']['mautic.page_on_hit']['description'])); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /tests/QueryBuilder/WhereBuilderTest.php: -------------------------------------------------------------------------------- 1 | $method($method, $val[0], $val[1]); 53 | $ignoreWhere = true; 54 | break; 55 | case 'isNull': 56 | case 'isNotNull': 57 | case 'isEmpty': 58 | case 'isNotEmpty': 59 | $val = null; 60 | break; 61 | default: 62 | $val = 1; 63 | } 64 | $clauses[] = [ 65 | 'col' => $method, 66 | 'expr' => $method, 67 | 'val' => $val, 68 | ]; 69 | 70 | if (empty($ignoreWhere)) { 71 | $whereBuilder->$method($method, $val); 72 | } 73 | } 74 | 75 | $this->assertEquals($clauses, $whereBuilder->getClauses()); 76 | 77 | $andX = $whereBuilder->andX(); 78 | $andX->eq('eq', 2) 79 | ->neq('neq', 2) 80 | ->between('between', 1, 2); 81 | 82 | $clauses[] = [ 83 | 'col' => null, 84 | 'expr' => 'andX', 85 | 'val' => [ 86 | [ 87 | 'col' => 'eq', 88 | 'expr' => 'eq', 89 | 'val' => 2, 90 | ], 91 | [ 92 | 'col' => 'neq', 93 | 'expr' => 'neq', 94 | 'val' => 2, 95 | ], 96 | [ 97 | 'col' => 'between', 98 | 'expr' => 'between', 99 | 'val' => [1, 2], 100 | ], 101 | ], 102 | ]; 103 | 104 | $whereBuilder->add($andX); 105 | 106 | $this->assertEquals($clauses, $whereBuilder->getClauses()); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /tests/ResponseTest.php: -------------------------------------------------------------------------------- 1 | ['Fri, 17 Nov 2017 14:09:31 GMT'], 23 | 'Server' => ['Apache/2.4.25 (Unix) OpenSSL/0.9.8zh PHP/7.0.15'], 24 | 'X-Powered-By' => ['PHP/7.0.15'], 25 | 'Set-Cookie' => ['9743595cf0a472cb3ec0272949ffe7e8=psh2rh9cam538t1u3e1gd3d8l3; path=/; HttpOnly'], 26 | 'Transfer-Encoding' => ['chunked'], 27 | 'Content-Type' => ['text/html; charset=UTF-8'], 28 | ]; 29 | 30 | private $htmlBody = ' 31 | 32 | 33 | 34 | 35 | 36 | hello world 37 | 38 | '; 39 | 40 | private $jsonBody = '{ 41 | "hello": "world" 42 | }'; 43 | 44 | private $urlParamBody = 'first=value&arr[]=foo+bar&arr[]=baz'; 45 | 46 | private function getHtmlResponse($code = 200) 47 | { 48 | return new HttpResponse($code, $this->headers, $this->htmlBody); 49 | } 50 | 51 | private function getJsonResponse($code = 200) 52 | { 53 | return new HttpResponse($code, $this->headers, $this->jsonBody); 54 | } 55 | 56 | private function getUrlParamResponse($code = 200) 57 | { 58 | return new HttpResponse($code, $this->headers, $this->urlParamBody); 59 | } 60 | 61 | public function testParseResponse() 62 | { 63 | $response = new Response($this->getJsonResponse()); 64 | $this->assertSame(200, $response->getStatusCode()); 65 | $this->assertSame($this->headers, $response->getHeaders()); 66 | $this->assertSame($this->jsonBody, $response->getBody()); 67 | } 68 | 69 | public function testValidation() 70 | { 71 | $this->expectException(UnexpectedResponseFormatException::class); 72 | $response = new Response($this->getHtmlResponse(500)); 73 | } 74 | 75 | public function testValidation404() 76 | { 77 | try { 78 | $response = new Response($this->getHtmlResponse(404)); 79 | } catch (UnexpectedResponseFormatException $e) { 80 | $this->assertSame(404, $e->getCode()); 81 | } 82 | } 83 | 84 | public function testDecodeFromUrlParamsWithParams() 85 | { 86 | $response = new Response($this->getUrlParamResponse()); 87 | $responseParams = $response->decodeFromUrlParams(); 88 | $this->assertSame('value', $responseParams['first']); 89 | } 90 | 91 | public function testDecodeFromUrlParamsWithNoParams() 92 | { 93 | $response = new Response(new HttpResponse(200, $this->headers)); 94 | $this->expectException(UnexpectedResponseFormatException::class); 95 | $response->decodeFromUrlParams(); 96 | } 97 | 98 | public function testDecodeFromJsonWithJson() 99 | { 100 | $response = new Response($this->getJsonResponse()); 101 | $json = $response->decodeFromJson(); 102 | $this->assertSame('world', $json['hello']); 103 | } 104 | 105 | public function testDecodeFromJsonWithEmptyJson() 106 | { 107 | $response = new Response(new HttpResponse(200, $this->headers)); 108 | $this->expectException(UnexpectedResponseFormatException::class); 109 | $response->decodeFromUrlParams(); 110 | } 111 | 112 | public function testGetDecodedBodyWithJson() 113 | { 114 | $response = new Response($this->getJsonResponse()); 115 | $body = $response->getDecodedBody(); 116 | $this->assertSame('world', $body['hello']); 117 | } 118 | 119 | public function testDecodeFromJsonWithEmptyResponse() 120 | { 121 | $response = new Response(new HttpResponse(200, $this->headers)); 122 | $this->expectException(UnexpectedResponseFormatException::class); 123 | $body = $response->getDecodedBody(); 124 | } 125 | 126 | public function testDecodeFromJsonWithTextResponse() 127 | { 128 | $response = new Response(new HttpResponse(200, $this->headers, 'OK')); 129 | $this->expectException(UnexpectedResponseFormatException::class); 130 | $body = $response->getDecodedBody(); 131 | } 132 | 133 | public function testSaveToFile() 134 | { 135 | $response = new Response($this->getJsonResponse()); 136 | $result = $response->saveToFile(sys_get_temp_dir()); 137 | $this->assertFalse(empty($result['file'])); 138 | $this->assertTrue(file_exists($result['file'])); 139 | $this->assertSame($this->jsonBody, file_get_contents($result['file'])); 140 | $this->assertTrue(unlink($result['file'])); 141 | } 142 | 143 | public function testIsZip() 144 | { 145 | $response = new Response($this->getJsonResponse()->withHeader('Content-Type', 'application/zip')); 146 | $this->assertTrue($response->isZip()); 147 | } 148 | 149 | public function testIsNotZip() 150 | { 151 | $response = new Response($this->getJsonResponse()->withHeader('Content-Type', 'application/json')); 152 | $this->assertFalse($response->isZip()); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | = 2.4 14 | | 15 | | Pass this as the second parameter to specify which to use. 16 | | 17 | | If commented out, will default OAuth. see MauticApiTestCase::getAuth() 18 | | 19 | */ 20 | // 'AuthMethod' => 'BasicAuth', 21 | 22 | /* 23 | |-------------------------------------------------------------------------- 24 | | Basic Authentication credentials 25 | |-------------------------------------------------------------------------- 26 | | 27 | | Basic Auth uses a simple username password method. 28 | | 29 | | It's important to note, that this is not recommended unless you are 30 | | using HTTPS as your username and password are exposed. 31 | | 32 | | BOTH the username and password are required. It's recommended that 33 | | you add a specific user and ensure a long passPhrase! 34 | | 35 | */ 36 | // 'userName' => '', 37 | // 'password' => '', 38 | 39 | /* 40 | |-------------------------------------------------------------------------- 41 | | OAuth credentials 42 | |-------------------------------------------------------------------------- 43 | | 44 | | The API supports both OAuth1a and OAuth2. 45 | | 46 | | I've found there is more control if you specify which version you want 47 | | to use. 48 | | 49 | | Required mainly for OAuth 1a testing; 'OAuth1a' or 'OAuth2' 50 | | 51 | */ 52 | 'version' => 'OAuth1a', 53 | 54 | // Required for OAuth1a and OAuth2 55 | 'baseUrl' => 'http://example.com/index.php', 56 | 57 | // Required for All tests 58 | 'apiUrl' => 'http://example.com/index.php/api/', 59 | // Required for EmailsTest 60 | 'testEmail' => 'notexisting@email.com', 61 | 62 | // Required for both OAuth 1a and 2 testing 63 | 'accessToken' => '', 64 | 65 | // Required only for OAuth 1a testing 66 | 'accessTokenSecret' => '', 67 | 'requestTokenUrl' => '', // Set with any string to force the library to use the OAuth1.a spec; leave blank to use OAuth2 68 | 'clientKey' => '', 69 | 'clientSecret' => '', 70 | 'callback' => '', 71 | 72 | // Required only for OAuth 2 testing. 73 | 'refreshToken' => '', 74 | ]; 75 | -------------------------------------------------------------------------------- /tests/mauticlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mautic/api-library/d0f6603a768f139cb17ed4655b3cabf9a1cd4b83/tests/mauticlogo.png --------------------------------------------------------------------------------