diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index fd8d6e1..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,143 +0,0 @@ -version: 2.1 -commands: - setup: - description: "Setup environment" - steps: - - run: | - composer self-update --2 - install_civicrm: - description: "Install CiviCRM" - parameters: - build_name: - type: string - default: master - type: - type: string - default: drupal-clean - version: - type: string - default: master - url: - type: string - default: http://localhost:8080 - steps: - - run: | - civiver=<< parameters.version >> - if [[ "$civiver" == "stable" ]]; then - civiver=$(curl -s 'https://latest.civicrm.org/stable.php') - fi - su - buildkit -c "/buildkit/bin/civibuild create << parameters.build_name >> --civi-ver $civiver --type << parameters.type >> --url << parameters.url >>" - install_extension: - description: "Install Extension" - parameters: - build_name: - type: string - default: master - steps: - - run: | - EXT_DIR=/buildkit/build/<< parameters.build_name >>/web/sites/default/files/civicrm/ext/$CIRCLE_PROJECT_REPONAME - cp /root/project $EXT_DIR -r - chown buildkit:buildkit $EXT_DIR -R - cd $EXT_DIR - composer install - su - buildkit -c "cd /buildkit/build/<< parameters.build_name >>/web && cv en $CIRCLE_PROJECT_REPONAME" - run_civilint: - description: "Run civilint" - parameters: - build_name: - type: string - default: master - steps: - - run: su - buildkit -c "cd /buildkit/build/<< parameters.build_name >>/web/sites/default/files/civicrm/ext/$CIRCLE_PROJECT_REPONAME && find . -type f -not -path './vendor/*' | civilint -" - run_phpunit: - description: "Run PHPUnit" - parameters: - build_name: - type: string - default: master - steps: - - run: | - mkdir -p /phpunit /coverage - chown buildkit:buildkit /phpunit /coverage - su - buildkit -c "cd /buildkit/build/<< parameters.build_name >>/web/sites/default/files/civicrm/ext/$CIRCLE_PROJECT_REPONAME && phpdbg -qrr /buildkit/bin/phpunit5 --log-junit /phpunit/<< parameters.build_name >>/junit.xml --coverage-html /coverage/<< parameters.build_name >>" - run_all: - description: "Run all steps" - parameters: - build_name: - type: string - default: master - type: - type: string - default: drupal-clean - version: - type: string - default: master - url: - type: string - default: http://localhost:8080 - steps: - - install_civicrm: - build_name: << parameters.build_name >> - type: << parameters.type >> - version: << parameters.version >> - url: << parameters.url >> - - install_extension: - build_name: << parameters.build_name >> - # TODO: enable civilint - # - run_civilint: - # build_name: << parameters.build_name >> - - run_phpunit: - build_name: << parameters.build_name >> - - store_test_results: - path: /phpunit - - store_artifacts: - path: /phpunit - - store_artifacts: - path: /coverage - -executors: - civicrm: - docker: - - image: michaelmcandrew/civicrm-buildkit - name: civicrm - environment: - TERM: xterm-color - APACHE_RUN_USER: buildkit - - image: mariadb:10.3 - name: mysql - environment: - MYSQL_ROOT_PASSWORD: buildkit - - image: djfarrelly/maildev - name: maildev - -jobs: - build: - executor: civicrm - steps: - - setup - - checkout - - run_all - - run_all: - build_name: civi-stable - version: "stable" - url: http://localhost:8081 - - run_all: - build_name: civi-5.39 - version: "5.39" - url: http://localhost:8082 - -workflows: - version: 2 - build: - jobs: - - build - nightly: - triggers: - - schedule: - cron: "0 5 * * *" - filters: - branches: - only: - - master - jobs: - - build diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml new file mode 100644 index 0000000..f3c5897 --- /dev/null +++ b/.github/workflows/unit-tests.yml @@ -0,0 +1,86 @@ +name: "Run unit tests" + +on: + push: + branches: + - master + tags: + pull_request: + +env: + CIVI_CI_CIVICRM: ${{ vars.CIVI_CI_CIVICRM || '["5.69", "master"]' }} + CIVI_CI_MYSQL: ${{ vars.CIVI_CI_MYSQL || '["8.0"]' }} + CIVI_CI_OS: ${{ vars.CIVI_CI_OS || '["ubuntu-22.04"]' }} + CIVI_CI_PHP: ${{ vars.CIVI_CI_PHP || '["7.4", "8.0", "8.1"]' }} + CIVI_CI_BUILD_TYPE: ${{ vars.CIVI_CI_BUILD_TYPE || '["drupal-clean"]' }} + CIVI_CI_EXCLUDES: ${{ vars.CIVI_CI_EXCLUDES || '' }} + +jobs: + setup-matrix: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.setup-matrix.outputs.matrix }} + steps: + - id: setup-matrix + uses: druzsan/setup-matrix@v2 + with: + matrix: | + civicrm: ${{ env.CIVI_CI_CIVICRM }} + mysql: ${{ env.CIVI_CI_MYSQL }} + os: ${{ env.CIVI_CI_OS }} + php: ${{ env.CIVI_CI_PHP }} + build-type: ${{ env.CIVI_CI_BUILD_TYPE }} + exclude: ${{ env.CIVI_CI_EXCLUDES }} + + run-tests: + needs: setup-matrix + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.setup-matrix.outputs.matrix) }} + runs-on: "${{ matrix.os }}" + + services: + mysql: + image: "mysql:${{ matrix.mysql }}" + env: + MYSQL_ALLOW_EMPTY_PASSWORD: "yes" + ports: + - "3306:3306" + options: >- + --health-cmd="mysqladmin ping" + --health-interval="10s" + --health-timeout="5s" + --health-retries="3" + + steps: + - id: clone-repo + name: "Clone the repository" + uses: "actions/checkout@v4" + with: + path: "at.greenpeace.mailingwork" + - id: build-ci + uses: greenpeace-cee/civi-ci@main + with: + extension: at.greenpeace.mailingwork + civicrm: ${{ matrix.civicrm }} + php: ${{ matrix.php }} + build-type: ${{ matrix.build-type }} + - id: install-extension + name: "Install Extension" + env: + EXT_DIR: ${{ steps.build-ci.outputs.ext-dir }} + run: | + PATH="/home/runner/buildkit/bin:$PATH" + cd "$EXT_DIR" + cp -R "$GITHUB_WORKSPACE/at.greenpeace.mailingwork" "$EXT_DIR/at.greenpeace.mailingwork" + git clone https://github.com/systopia/de.systopia.identitytracker.git "$EXT_DIR/de.systopia.identitytracker" + composer install --working-dir="$EXT_DIR/at.greenpeace.mailingwork" + cv en at.greenpeace.mailingwork + - id: run-tests + name: "Run Tests" + env: + EXT_DIR: ${{ steps.build-ci.outputs.ext-dir }} + run: | + PATH="/home/runner/buildkit/bin:$PATH" + cd "$EXT_DIR/at.greenpeace.mailingwork" + CIVICRM_UF="UnitTests" phpunit9 diff --git a/CRM/Mailingwork/Processor/Greenpeace/Clicks.php b/CRM/Mailingwork/Processor/Greenpeace/Clicks.php index f5fee8e..a711a11 100644 --- a/CRM/Mailingwork/Processor/Greenpeace/Clicks.php +++ b/CRM/Mailingwork/Processor/Greenpeace/Clicks.php @@ -30,7 +30,7 @@ public function import() { 'completed' => $this->getOrCreateOptionValue('mailingwork_mailing_sync_status', 'completed'), ]; - $mailingQuery = Api4\MailingworkMailing::get() + $mailingQuery = Api4\MailingworkMailing::get(FALSE) ->addSelect('*', 'click_sync_status_id:name') ->addWhere('recipient_sync_status_id', 'IN', [ $syncStatuses['in_progress'], @@ -84,7 +84,7 @@ public function import() { private function getActivityContactID(int $mailingID, int $contactID) { $activityTargetOptValue = $this->getOrCreateOptionValue('activity_contacts', 'Activity Targets'); - $activityQuery = Api4\Activity::get() + $activityQuery = Api4\Activity::get(FALSE) ->addSelect('activity_contact.id') ->addJoin( 'ActivityContact AS activity_contact', 'INNER', ['id', '=', 'activity_contact.activity_id'] @@ -152,7 +152,7 @@ private function importMailingClicks(array $mailing) { if (self::isDuplicateClick($click->date, $activityContactID, $linkID)) { Civi::log()->info('[Mailingwork/Clicks] Found click with existing MailingworkClick, skipping'); } else { - Api4\MailingworkClick::create() + Api4\MailingworkClick::create(FALSE) ->addValue('activity_contact_id', $activityContactID) ->addValue('click_date', $click->date) ->addValue('link_id', $linkID) @@ -189,7 +189,7 @@ private function importMailingLinks($mailing) { foreach ($links as $link) { - $linksQuery = Api4\MailingworkLink::get() + $linksQuery = Api4\MailingworkLink::get(FALSE) ->addSelect('id') ->addWhere('mailing_id', '=', $mailing['id']) ->addWhere('mailingwork_id', '=', $link->id) @@ -197,7 +197,7 @@ private function importMailingLinks($mailing) { ->execute(); if ($linksQuery->count() < 1) { - $createdLink = Api4\MailingworkLink::create() + $createdLink = Api4\MailingworkLink::create(FALSE) ->addValue('mailing_id', $mailing['id']) ->addValue('mailingwork_id', $link->id) ->addValue('url', $link->url) @@ -214,7 +214,7 @@ private function importMailingLinks($mailing) { private function importLinkInterests(int $linkID, array $interests) { foreach ($interests as $interest) { - $linkInterestsQuery = Api4\MailingworkLinkInterest::get() + $linkInterestsQuery = Api4\MailingworkLinkInterest::get(FALSE) ->addJoin( 'MailingworkInterest AS mailingwork_interest', 'LEFT', @@ -227,7 +227,7 @@ private function importLinkInterests(int $linkID, array $interests) { if ($linkInterestsQuery->count() > 0) continue; - $interestsQuery = Api4\MailingworkInterest::get() + $interestsQuery = Api4\MailingworkInterest::get(FALSE) ->addSelect('id') ->addWhere('mailingwork_id', '=', $interest->id) ->setLimit(1) @@ -237,7 +237,7 @@ private function importLinkInterests(int $linkID, array $interests) { $interestID = $interestExists ? $interestsQuery->first()['id'] : NULL; if (!$interestExists) { - $createdInterest = Api4\MailingworkInterest::create() + $createdInterest = Api4\MailingworkInterest::create(FALSE) ->addValue('mailingwork_id', $interest->id) ->addValue('name', $interest->name) ->execute() @@ -246,7 +246,7 @@ private function importLinkInterests(int $linkID, array $interests) { $interestID = $createdInterest['id']; } - $createLinkInterestResult = Api4\MailingworkLinkInterest::create() + $createLinkInterestResult = Api4\MailingworkLinkInterest::create(FALSE) ->addValue('interest_id', $interestID) ->addValue('link_id', $linkID) ->execute(); @@ -270,7 +270,7 @@ private function updateMailingClickSyncStatus(int $mailingID, string $lastClickD 'in_progress' ); - Api4\MailingworkMailing::update() + Api4\MailingworkMailing::update(FALSE) ->addWhere('id', '=', $mailingID) ->addValue('click_sync_date', $lastClickDate) ->addValue('click_sync_status_id', $statusInProgress) @@ -278,14 +278,14 @@ private function updateMailingClickSyncStatus(int $mailingID, string $lastClickD } private static function setMailingClicksCompleted($mailingID) { - Api4\MailingworkMailing::update() + Api4\MailingworkMailing::update(FALSE) ->addValue('click_sync_status_id:name', 'completed') ->addWhere('id', '=', $mailingID) ->execute(); } private static function isDuplicateClick($clickDate, $activityContactID, $linkID) { - $count = Api4\MailingworkClick::get() + $count = Api4\MailingworkClick::get(FALSE) ->addWhere('activity_contact_id', '=', $activityContactID) ->addWhere('click_date', '=', $clickDate) ->addWhere('link_id', '=', $linkID) diff --git a/CRM/Mailingwork/Processor/Greenpeace/Openings.php b/CRM/Mailingwork/Processor/Greenpeace/Openings.php index f79cc62..cc58e94 100644 --- a/CRM/Mailingwork/Processor/Greenpeace/Openings.php +++ b/CRM/Mailingwork/Processor/Greenpeace/Openings.php @@ -131,7 +131,7 @@ private function importMailingOpenings($mailing) { Civi::log()->warning('[Mailingwork/Openings] Ignoring opening without matching activity'); continue; } - $dupeOpening = MailingworkOpening::get() + $dupeOpening = MailingworkOpening::get(FALSE) ->addSelect('id') ->addWhere('activity_contact_id', '=', $parent_activity['activity_contact_id']) ->addWhere('opening_date', '=', $opening['date']) @@ -139,11 +139,11 @@ private function importMailingOpenings($mailing) { ->first(); if (!empty($dupeOpening)) { Civi::log()->info('[Mailingwork/Openings] Found opening with existing MailingworkOpening, merging'); - $apiOpening = MailingworkOpening::update() + $apiOpening = MailingworkOpening::update(FALSE) ->addWhere('id', '=', $dupeOpening['id']); } else { - $apiOpening = MailingworkOpening::create(); + $apiOpening = MailingworkOpening::create(FALSE); $stored_count++; } $apiOpening->addValue('activity_contact_id', $parent_activity['activity_contact_id']) diff --git a/CRM/Mailingwork/Processor/Greenpeace/Optouts.php b/CRM/Mailingwork/Processor/Greenpeace/Optouts.php index ebf3a66..a217e2c 100644 --- a/CRM/Mailingwork/Processor/Greenpeace/Optouts.php +++ b/CRM/Mailingwork/Processor/Greenpeace/Optouts.php @@ -170,7 +170,7 @@ private function removeGroups(array $contacts, array $groups_to_remove, array $o ->addValue('optout_information.optout_item', $optout[self::EMAIL_FIELD]) ->addValue('subject', "Opt-Out from \"{$group_title}\" via Mailingwork") ->addValue('source_record_id', $groupContact['group_id']) - ->addChain('activity_contact', ActivityContact::create() + ->addChain('activity_contact', ActivityContact::create(FALSE) ->addValue('activity_id', '$id') ->addValue('contact_id', $groupContact['contact_id']) ->addValue('record_type_id', 3) @@ -204,7 +204,7 @@ private function setSuppression(array $contacts, $suppression, array $optout, ar $suppressedContacts = Contact::get(FALSE) ->addWhere('id', 'IN', $contacts) ->addWhere($suppression, '=', 0) - ->addChain('set_suppression', Contact::update() + ->addChain('set_suppression', Contact::update(FALSE) ->addWhere('id', '=', '$id') ->addValue($suppression, 1) ) @@ -235,7 +235,7 @@ private function setSuppression(array $contacts, $suppression, array $optout, ar ->addValue('optout_information.optout_identifier', $mailing['id']) ->addValue('optout_information.optout_item', $optout[self::EMAIL_FIELD]) ->addValue('subject', "Added \"{$suppression_title}\" via Mailingwork") - ->addChain('activity_contact', ActivityContact::create() + ->addChain('activity_contact', ActivityContact::create(FALSE) ->addValue('activity_id', '$id') ->addValue('contact_id', $contact['id']) ->addValue('record_type_id', 3) @@ -265,9 +265,8 @@ private function setSuppression(array $contacts, $suppression, array $optout, ar } private function prepareGroupCache() { - $groups = Group::get() + $groups = Group::get(FALSE) ->addSelect('id', 'title') - ->setCheckPermissions(FALSE) ->execute(); foreach ($groups as $group) { $this->groups[$group['id']] = $group['title']; diff --git a/CRM/Mailingwork/Upgrader.php b/CRM/Mailingwork/Upgrader.php index 911cca3..c64552c 100644 --- a/CRM/Mailingwork/Upgrader.php +++ b/CRM/Mailingwork/Upgrader.php @@ -3,27 +3,7 @@ /** * Collection of upgrade steps. */ -class CRM_Mailingwork_Upgrader extends CRM_Mailingwork_Upgrader_Base { - - /** - * Add a column to a table if it doesn't already exist - * - * @param $table - * @param $column - * @param $properties - * - * @return bool - */ - protected function addColumn($table, $column, $properties) { - $queries = []; - if (!CRM_Core_BAO_SchemaHandler::checkIfFieldExists($table, $column)) { - $queries[] = "ALTER TABLE `$table` ADD COLUMN `$column` $properties"; - foreach ($queries as $query) { - CRM_Core_DAO::executeQuery($query, array(), TRUE, NULL, FALSE, FALSE); - } - } - return TRUE; - } +class CRM_Mailingwork_Upgrader extends CRM_Extension_Upgrader_Base { /** * Add bounce_sync_date and bounce_sync_status_id diff --git a/CRM/Mailingwork/Upgrader/Base.php b/CRM/Mailingwork/Upgrader/Base.php deleted file mode 100644 index 0130f85..0000000 --- a/CRM/Mailingwork/Upgrader/Base.php +++ /dev/null @@ -1,396 +0,0 @@ -ctx = array_shift($args); - $instance->queue = $instance->ctx->queue; - $method = array_shift($args); - return call_user_func_array([$instance, $method], $args); - } - - /** - * CRM_Mailingwork_Upgrader_Base constructor. - * - * @param $extensionName - * @param $extensionDir - */ - public function __construct($extensionName, $extensionDir) { - $this->extensionName = $extensionName; - $this->extensionDir = $extensionDir; - } - - // ******** Task helpers ******** - - /** - * Run a CustomData file. - * - * @param string $relativePath - * the CustomData XML file path (relative to this extension's dir) - * @return bool - */ - public function executeCustomDataFile($relativePath) { - $xml_file = $this->extensionDir . '/' . $relativePath; - return $this->executeCustomDataFileByAbsPath($xml_file); - } - - /** - * Run a CustomData file - * - * @param string $xml_file - * the CustomData XML file path (absolute path) - * - * @return bool - */ - protected function executeCustomDataFileByAbsPath($xml_file) { - $import = new CRM_Utils_Migrate_Import(); - $import->run($xml_file); - return TRUE; - } - - /** - * Run a SQL file. - * - * @param string $relativePath - * the SQL file path (relative to this extension's dir) - * - * @return bool - */ - public function executeSqlFile($relativePath) { - CRM_Utils_File::sourceSQLFile( - CIVICRM_DSN, - $this->extensionDir . DIRECTORY_SEPARATOR . $relativePath - ); - return TRUE; - } - - /** - * Run the sql commands in the specified file. - * - * @param string $tplFile - * The SQL file path (relative to this extension's dir). - * Ex: "sql/mydata.mysql.tpl". - * - * @return bool - * @throws \CRM_Core_Exception - */ - public function executeSqlTemplate($tplFile) { - // Assign multilingual variable to Smarty. - $upgrade = new CRM_Upgrade_Form(); - - $tplFile = CRM_Utils_File::isAbsolute($tplFile) ? $tplFile : $this->extensionDir . DIRECTORY_SEPARATOR . $tplFile; - $smarty = CRM_Core_Smarty::singleton(); - $smarty->assign('domainID', CRM_Core_Config::domainID()); - CRM_Utils_File::sourceSQLFile( - CIVICRM_DSN, $smarty->fetch($tplFile), NULL, TRUE - ); - return TRUE; - } - - /** - * Run one SQL query. - * - * This is just a wrapper for CRM_Core_DAO::executeSql, but it - * provides syntactic sugar for queueing several tasks that - * run different queries - * - * @return bool - */ - public function executeSql($query, $params = []) { - // FIXME verify that we raise an exception on error - CRM_Core_DAO::executeQuery($query, $params); - return TRUE; - } - - /** - * Syntactic sugar for enqueuing a task which calls a function in this class. - * - * The task is weighted so that it is processed - * as part of the currently-pending revision. - * - * After passing the $funcName, you can also pass parameters that will go to - * the function. Note that all params must be serializable. - */ - public function addTask($title) { - $args = func_get_args(); - $title = array_shift($args); - $task = new CRM_Queue_Task( - [get_class($this), '_queueAdapter'], - $args, - $title - ); - return $this->queue->createItem($task, ['weight' => -1]); - } - - // ******** Revision-tracking helpers ******** - - /** - * Determine if there are any pending revisions. - * - * @return bool - */ - public function hasPendingRevisions() { - $revisions = $this->getRevisions(); - $currentRevision = $this->getCurrentRevision(); - - if (empty($revisions)) { - return FALSE; - } - if (empty($currentRevision)) { - return TRUE; - } - - return ($currentRevision < max($revisions)); - } - - /** - * Add any pending revisions to the queue. - * - * @param CRM_Queue_Queue $queue - */ - public function enqueuePendingRevisions(CRM_Queue_Queue $queue) { - $this->queue = $queue; - - $currentRevision = $this->getCurrentRevision(); - foreach ($this->getRevisions() as $revision) { - if ($revision > $currentRevision) { - $title = E::ts('Upgrade %1 to revision %2', [ - 1 => $this->extensionName, - 2 => $revision, - ]); - - // note: don't use addTask() because it sets weight=-1 - - $task = new CRM_Queue_Task( - [get_class($this), '_queueAdapter'], - ['upgrade_' . $revision], - $title - ); - $this->queue->createItem($task); - - $task = new CRM_Queue_Task( - [get_class($this), '_queueAdapter'], - ['setCurrentRevision', $revision], - $title - ); - $this->queue->createItem($task); - } - } - } - - /** - * Get a list of revisions. - * - * @return array - * revisionNumbers sorted numerically - */ - public function getRevisions() { - if (!is_array($this->revisions)) { - $this->revisions = []; - - $clazz = new ReflectionClass(get_class($this)); - $methods = $clazz->getMethods(); - foreach ($methods as $method) { - if (preg_match('/^upgrade_(.*)/', $method->name, $matches)) { - $this->revisions[] = $matches[1]; - } - } - sort($this->revisions, SORT_NUMERIC); - } - - return $this->revisions; - } - - public function getCurrentRevision() { - $revision = CRM_Core_BAO_Extension::getSchemaVersion($this->extensionName); - if (!$revision) { - $revision = $this->getCurrentRevisionDeprecated(); - } - return $revision; - } - - private function getCurrentRevisionDeprecated() { - $key = $this->extensionName . ':version'; - if ($revision = \Civi::settings()->get($key)) { - $this->revisionStorageIsDeprecated = TRUE; - } - return $revision; - } - - public function setCurrentRevision($revision) { - CRM_Core_BAO_Extension::setSchemaVersion($this->extensionName, $revision); - // clean up legacy schema version store (CRM-19252) - $this->deleteDeprecatedRevision(); - return TRUE; - } - - private function deleteDeprecatedRevision() { - if ($this->revisionStorageIsDeprecated) { - $setting = new CRM_Core_BAO_Setting(); - $setting->name = $this->extensionName . ':version'; - $setting->delete(); - CRM_Core_Error::debug_log_message("Migrated extension schema revision ID for {$this->extensionName} from civicrm_setting (deprecated) to civicrm_extension.\n"); - } - } - - // ******** Hook delegates ******** - - /** - * @see https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_install - */ - public function onInstall() { - $files = glob($this->extensionDir . '/sql/*_install.sql'); - if (is_array($files)) { - foreach ($files as $file) { - CRM_Utils_File::sourceSQLFile(CIVICRM_DSN, $file); - } - } - $files = glob($this->extensionDir . '/sql/*_install.mysql.tpl'); - if (is_array($files)) { - foreach ($files as $file) { - $this->executeSqlTemplate($file); - } - } - $files = glob($this->extensionDir . '/xml/*_install.xml'); - if (is_array($files)) { - foreach ($files as $file) { - $this->executeCustomDataFileByAbsPath($file); - } - } - if (is_callable([$this, 'install'])) { - $this->install(); - } - } - - /** - * @see https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall - */ - public function onPostInstall() { - $revisions = $this->getRevisions(); - if (!empty($revisions)) { - $this->setCurrentRevision(max($revisions)); - } - if (is_callable([$this, 'postInstall'])) { - $this->postInstall(); - } - } - - /** - * @see https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_uninstall - */ - public function onUninstall() { - $files = glob($this->extensionDir . '/sql/*_uninstall.mysql.tpl'); - if (is_array($files)) { - foreach ($files as $file) { - $this->executeSqlTemplate($file); - } - } - if (is_callable([$this, 'uninstall'])) { - $this->uninstall(); - } - $files = glob($this->extensionDir . '/sql/*_uninstall.sql'); - if (is_array($files)) { - foreach ($files as $file) { - CRM_Utils_File::sourceSQLFile(CIVICRM_DSN, $file); - } - } - } - - /** - * @see https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_enable - */ - public function onEnable() { - // stub for possible future use - if (is_callable([$this, 'enable'])) { - $this->enable(); - } - } - - /** - * @see https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_disable - */ - public function onDisable() { - // stub for possible future use - if (is_callable([$this, 'disable'])) { - $this->disable(); - } - } - - public function onUpgrade($op, CRM_Queue_Queue $queue = NULL) { - switch ($op) { - case 'check': - return [$this->hasPendingRevisions()]; - - case 'enqueue': - return $this->enqueuePendingRevisions($queue); - - default: - } - } - -} diff --git a/README.md b/README.md index ac539e9..5d76c81 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # CiviCRM Mailingwork Integration -[![CircleCI](https://circleci.com/gh/greenpeace-cee/at.greenpeace.mailingwork.svg?style=svg)](https://circleci.com/gh/greenpeace-cee/at.greenpeace.mailingwork) +[![Run unit tests](https://github.com/greenpeace-cee/at.greenpeace.mailingwork/actions/workflows/unit-tests.yml/badge.svg)](https://github.com/greenpeace-cee/at.greenpeace.mailingwork/actions/workflows/unit-tests.yml) + This extension connects CiviCRM with [Mailingwork](https://mailingwork.de/), an email marketing tool. diff --git a/info.xml b/info.xml index 250e56e..1f93c5f 100644 --- a/info.xml +++ b/info.xml @@ -23,14 +23,18 @@ + CRM/Mailingwork - 22.05.2 + 23.02.1 menu-xml@1.0.0 mgd-php@1.0.0 setting-php@1.0.0 + smarty-v2@1.0.1 + entity-types-php@1.0.0 + CRM_Mailingwork_Upgrader diff --git a/mailingwork.civix.php b/mailingwork.civix.php index b3f3c1a..d0ce1b7 100644 --- a/mailingwork.civix.php +++ b/mailingwork.civix.php @@ -24,7 +24,7 @@ class CRM_Mailingwork_ExtensionUtil { * Translated text. * @see ts */ - public static function ts($text, $params = []) { + public static function ts($text, $params = []): string { if (!array_key_exists('domain', $params)) { $params['domain'] = [self::LONG_NAME, NULL]; } @@ -41,7 +41,7 @@ public static function ts($text, $params = []) { * Ex: 'http://example.org/sites/default/ext/org.example.foo'. * Ex: 'http://example.org/sites/default/ext/org.example.foo/css/foo.css'. */ - public static function url($file = NULL) { + public static function url($file = NULL): string { if ($file === NULL) { return rtrim(CRM_Core_Resources::singleton()->getUrl(self::LONG_NAME), '/'); } @@ -79,40 +79,22 @@ public static function findClass($suffix) { use CRM_Mailingwork_ExtensionUtil as E; -function _mailingwork_civix_mixin_polyfill() { - if (!class_exists('CRM_Extension_MixInfo')) { - $polyfill = __DIR__ . '/mixin/polyfill.php'; - (require $polyfill)(E::LONG_NAME, E::SHORT_NAME, E::path()); - } -} - /** * (Delegated) Implements hook_civicrm_config(). * * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config */ -function _mailingwork_civix_civicrm_config(&$config = NULL) { +function _mailingwork_civix_civicrm_config($config = NULL) { static $configured = FALSE; if ($configured) { return; } $configured = TRUE; - $template = CRM_Core_Smarty::singleton(); - $extRoot = __DIR__ . DIRECTORY_SEPARATOR; - $extDir = $extRoot . 'templates'; - - if (is_array($template->template_dir)) { - array_unshift($template->template_dir, $extDir); - } - else { - $template->template_dir = [$extDir, $template->template_dir]; - } - $include_path = $extRoot . PATH_SEPARATOR . get_include_path(); set_include_path($include_path); - _mailingwork_civix_mixin_polyfill(); + // Based on , this does not currently require mixin/polyfill.php. } /** @@ -122,36 +104,7 @@ function _mailingwork_civix_civicrm_config(&$config = NULL) { */ function _mailingwork_civix_civicrm_install() { _mailingwork_civix_civicrm_config(); - if ($upgrader = _mailingwork_civix_upgrader()) { - $upgrader->onInstall(); - } - _mailingwork_civix_mixin_polyfill(); -} - -/** - * Implements hook_civicrm_postInstall(). - * - * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_postInstall - */ -function _mailingwork_civix_civicrm_postInstall() { - _mailingwork_civix_civicrm_config(); - if ($upgrader = _mailingwork_civix_upgrader()) { - if (is_callable([$upgrader, 'onPostInstall'])) { - $upgrader->onPostInstall(); - } - } -} - -/** - * Implements hook_civicrm_uninstall(). - * - * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_uninstall - */ -function _mailingwork_civix_civicrm_uninstall() { - _mailingwork_civix_civicrm_config(); - if ($upgrader = _mailingwork_civix_upgrader()) { - $upgrader->onUninstall(); - } + // Based on , this does not currently require mixin/polyfill.php. } /** @@ -159,59 +112,9 @@ function _mailingwork_civix_civicrm_uninstall() { * * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_enable */ -function _mailingwork_civix_civicrm_enable() { - _mailingwork_civix_civicrm_config(); - if ($upgrader = _mailingwork_civix_upgrader()) { - if (is_callable([$upgrader, 'onEnable'])) { - $upgrader->onEnable(); - } - } - _mailingwork_civix_mixin_polyfill(); -} - -/** - * (Delegated) Implements hook_civicrm_disable(). - * - * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_disable - * @return mixed - */ -function _mailingwork_civix_civicrm_disable() { +function _mailingwork_civix_civicrm_enable(): void { _mailingwork_civix_civicrm_config(); - if ($upgrader = _mailingwork_civix_upgrader()) { - if (is_callable([$upgrader, 'onDisable'])) { - $upgrader->onDisable(); - } - } -} - -/** - * (Delegated) Implements hook_civicrm_upgrade(). - * - * @param $op string, the type of operation being performed; 'check' or 'enqueue' - * @param $queue CRM_Queue_Queue, (for 'enqueue') the modifiable list of pending up upgrade tasks - * - * @return mixed - * based on op. for 'check', returns array(boolean) (TRUE if upgrades are pending) - * for 'enqueue', returns void - * - * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_upgrade - */ -function _mailingwork_civix_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) { - if ($upgrader = _mailingwork_civix_upgrader()) { - return $upgrader->onUpgrade($op, $queue); - } -} - -/** - * @return CRM_Mailingwork_Upgrader - */ -function _mailingwork_civix_upgrader() { - if (!file_exists(__DIR__ . '/CRM/Mailingwork/Upgrader.php')) { - return NULL; - } - else { - return CRM_Mailingwork_Upgrader_Base::instance(); - } + // Based on , this does not currently require mixin/polyfill.php. } /** @@ -230,8 +133,8 @@ function _mailingwork_civix_insert_navigation_menu(&$menu, $path, $item) { if (empty($path)) { $menu[] = [ 'attributes' => array_merge([ - 'label' => CRM_Utils_Array::value('name', $item), - 'active' => 1, + 'label' => $item['name'] ?? NULL, + 'active' => 1, ], $item), ]; return TRUE; @@ -295,55 +198,3 @@ function _mailingwork_civix_fixNavigationMenuItems(&$nodes, &$maxNavID, $parentI } } } - -/** - * (Delegated) Implements hook_civicrm_entityTypes(). - * - * Find any *.entityType.php files, merge their content, and return. - * - * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_entityTypes - */ -function _mailingwork_civix_civicrm_entityTypes(&$entityTypes) { - $entityTypes = array_merge($entityTypes, [ - 'CRM_Mailingwork_DAO_ActivityContactEmail' => [ - 'name' => 'ActivityContactEmail', - 'class' => 'CRM_Mailingwork_DAO_ActivityContactEmail', - 'table' => 'civicrm_activity_contact_email', - ], - 'CRM_Mailingwork_DAO_MailingworkClick' => [ - 'name' => 'MailingworkClick', - 'class' => 'CRM_Mailingwork_DAO_MailingworkClick', - 'table' => 'civicrm_mailingwork_click', - ], - 'CRM_Mailingwork_DAO_MailingworkFolder' => [ - 'name' => 'MailingworkFolder', - 'class' => 'CRM_Mailingwork_DAO_MailingworkFolder', - 'table' => 'civicrm_mailingwork_folder', - ], - 'CRM_Mailingwork_DAO_MailingworkInterest' => [ - 'name' => 'MailingworkInterest', - 'class' => 'CRM_Mailingwork_DAO_MailingworkInterest', - 'table' => 'civicrm_mailingwork_interest', - ], - 'CRM_Mailingwork_DAO_MailingworkLink' => [ - 'name' => 'MailingworkLink', - 'class' => 'CRM_Mailingwork_DAO_MailingworkLink', - 'table' => 'civicrm_mailingwork_link', - ], - 'CRM_Mailingwork_DAO_MailingworkLinkInterest' => [ - 'name' => 'MailingworkLinkInterest', - 'class' => 'CRM_Mailingwork_DAO_MailingworkLinkInterest', - 'table' => 'civicrm_mailingwork_link_interest', - ], - 'CRM_Mailingwork_DAO_MailingworkMailing' => [ - 'name' => 'MailingworkMailing', - 'class' => 'CRM_Mailingwork_DAO_MailingworkMailing', - 'table' => 'civicrm_mailingwork_mailing', - ], - 'CRM_Mailingwork_DAO_MailingworkOpening' => [ - 'name' => 'MailingworkOpening', - 'class' => 'CRM_Mailingwork_DAO_MailingworkOpening', - 'table' => 'civicrm_mailingwork_opening', - ], - ]); -} diff --git a/mailingwork.php b/mailingwork.php index af69f89..d6b6ef7 100644 --- a/mailingwork.php +++ b/mailingwork.php @@ -23,24 +23,6 @@ function mailingwork_civicrm_install() { _mailingwork_civix_civicrm_install(); } -/** - * Implements hook_civicrm_postInstall(). - * - * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_postInstall - */ -function mailingwork_civicrm_postInstall() { - _mailingwork_civix_civicrm_postInstall(); -} - -/** - * Implements hook_civicrm_uninstall(). - * - * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_uninstall - */ -function mailingwork_civicrm_uninstall() { - _mailingwork_civix_civicrm_uninstall(); -} - /** * Implements hook_civicrm_enable(). * @@ -50,35 +32,6 @@ function mailingwork_civicrm_enable() { _mailingwork_civix_civicrm_enable(); } -/** - * Implements hook_civicrm_disable(). - * - * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_disable - */ -function mailingwork_civicrm_disable() { - _mailingwork_civix_civicrm_disable(); -} - -/** - * Implements hook_civicrm_upgrade(). - * - * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_upgrade - */ -function mailingwork_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) { - return _mailingwork_civix_civicrm_upgrade($op, $queue); -} - -/** - * Implements hook_civicrm_entityTypes(). - * - * Declare entity types provided by this module. - * - * @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_entityTypes - */ -function mailingwork_civicrm_entityTypes(&$entityTypes) { - _mailingwork_civix_civicrm_entityTypes($entityTypes); -} - /** * Implements hook_civicrm_navigationMenu(). * diff --git a/mixin/menu-xml@1.0.0.mixin.php b/mixin/menu-xml@1.0.0.mixin.php deleted file mode 100644 index 4c0b227..0000000 --- a/mixin/menu-xml@1.0.0.mixin.php +++ /dev/null @@ -1,31 +0,0 @@ -addListener('hook_civicrm_xmlMenu', function ($e) use ($mixInfo) { - if (!$mixInfo->isActive()) { - return; - } - - $files = (array) glob($mixInfo->getPath('xml/Menu/*.xml')); - foreach ($files as $file) { - $e->files[] = $file; - } - }); - -}; diff --git a/mixin/mgd-php@1.0.0.mixin.php b/mixin/mgd-php@1.0.0.mixin.php deleted file mode 100644 index 39d45b1..0000000 --- a/mixin/mgd-php@1.0.0.mixin.php +++ /dev/null @@ -1,42 +0,0 @@ -addListener('hook_civicrm_managed', function ($event) use ($mixInfo) { - // When deactivating on a polyfill/pre-mixin system, listeners may not cleanup automatically. - if (!$mixInfo->isActive()) { - return; - } - - $mgdFiles = CRM_Utils_File::findFiles($mixInfo->getPath(), '*.mgd.php'); - sort($mgdFiles); - foreach ($mgdFiles as $file) { - $es = include $file; - foreach ($es as $e) { - if (empty($e['module'])) { - $e['module'] = $mixInfo->longName; - } - if (empty($e['params']['version'])) { - $e['params']['version'] = '3'; - } - $event->entities[] = $e; - } - } - }); - -}; diff --git a/mixin/polyfill.php b/mixin/polyfill.php deleted file mode 100644 index f57c5eb..0000000 --- a/mixin/polyfill.php +++ /dev/null @@ -1,101 +0,0 @@ -')) { - $mixinVers[$name] = $ver; - } - } - $mixins = []; - foreach ($mixinVers as $name => $ver) { - $mixins[] = "$name@$ver"; - } - - // Imitate CRM_Extension_MixInfo. - $mixInfo = new class() { - - /** - * @var string - */ - public $longName; - - /** - * @var string - */ - public $shortName; - - public $_basePath; - - public function getPath($file = NULL) { - return $this->_basePath . ($file === NULL ? '' : (DIRECTORY_SEPARATOR . $file)); - } - - public function isActive() { - return \CRM_Extension_System::singleton()->getMapper()->isActiveModule($this->shortName); - } - - }; - $mixInfo->longName = $longName; - $mixInfo->shortName = $shortName; - $mixInfo->_basePath = $basePath; - - // Imitate CRM_Extension_BootCache. - $bootCache = new class() { - - public function define($name, $callback) { - $envId = \CRM_Core_Config_Runtime::getId(); - $oldExtCachePath = \Civi::paths()->getPath("[civicrm.compile]/CachedExtLoader.{$envId}.php"); - $stat = stat($oldExtCachePath); - $file = Civi::paths()->getPath('[civicrm.compile]/CachedMixin.' . md5($name . ($stat['mtime'] ?? 0)) . '.php'); - if (file_exists($file)) { - return include $file; - } - else { - $data = $callback(); - file_put_contents($file, '<' . "?php\nreturn " . var_export($data, 1) . ';'); - return $data; - } - } - - }; - - // Imitate CRM_Extension_MixinLoader::run() - // Parse all live mixins before trying to scan any classes. - global $_CIVIX_MIXIN_POLYFILL; - foreach ($mixins as $mixin) { - // If the exact same mixin is defined by multiple exts, just use the first one. - if (!isset($_CIVIX_MIXIN_POLYFILL[$mixin])) { - $_CIVIX_MIXIN_POLYFILL[$mixin] = include_once $basePath . '/mixin/' . $mixin . '.mixin.php'; - } - } - foreach ($mixins as $mixin) { - // If there's trickery about installs/uninstalls/resets, then we may need to register a second time. - if (!isset(\Civi::$statics[__FUNCTION__][$mixin])) { - \Civi::$statics[__FUNCTION__][$mixin] = 1; - $func = $_CIVIX_MIXIN_POLYFILL[$mixin]; - $func($mixInfo, $bootCache); - } - } -}; diff --git a/mixin/setting-php@1.0.0.mixin.php b/mixin/setting-php@1.0.0.mixin.php deleted file mode 100644 index 7195af4..0000000 --- a/mixin/setting-php@1.0.0.mixin.php +++ /dev/null @@ -1,32 +0,0 @@ -addListener('hook_civicrm_alterSettingsFolders', function ($e) use ($mixInfo) { - // When deactivating on a polyfill/pre-mixin system, listeners may not cleanup automatically. - if (!$mixInfo->isActive()) { - return; - } - - $settingsDir = $mixInfo->getPath('settings'); - if (!in_array($settingsDir, $e->settingsFolders) && is_dir($settingsDir)) { - $e->settingsFolders[] = $settingsDir; - } - }); - -}; diff --git a/mixin/smarty-v2@1.0.1.mixin.php b/mixin/smarty-v2@1.0.1.mixin.php new file mode 100644 index 0000000..5972dbd --- /dev/null +++ b/mixin/smarty-v2@1.0.1.mixin.php @@ -0,0 +1,51 @@ +getPath('templates'); + if (!file_exists($dir)) { + return; + } + + $register = function() use ($dir) { + // This implementation has a theoretical edge-case bug on older versions of CiviCRM where a template could + // be registered more than once. + CRM_Core_Smarty::singleton()->addTemplateDir($dir); + }; + + // Let's figure out what environment we're in -- so that we know the best way to call $register(). + + if (!empty($GLOBALS['_CIVIX_MIXIN_POLYFILL'])) { + // Polyfill Loader (v<=5.45): We're already in the middle of firing `hook_config`. + if ($mixInfo->isActive()) { + $register(); + } + return; + } + + if (CRM_Extension_System::singleton()->getManager()->extensionIsBeingInstalledOrEnabled($mixInfo->longName)) { + // New Install, Standard Loader: The extension has just been enabled, and we're now setting it up. + // System has already booted. New templates may be needed for upcoming installation steps. + $register(); + return; + } + + // Typical Pageview, Standard Loader: Defer the actual registration for a moment -- to ensure that Smarty is online. + \Civi::dispatcher()->addListener('hook_civicrm_config', function() use ($mixInfo, $register) { + if ($mixInfo->isActive()) { + $register(); + } + }); + +}; diff --git a/phpunit.xml.dist b/phpunit.xml.dist index fc8f870..19f6e17 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,15 +1,15 @@ - + + + + ./ + + ./tests/phpunit - - - ./ - - diff --git a/tests/phpunit/CRM/Mailingwork/Processor/Greenpeace/ClicksTest.php b/tests/phpunit/CRM/Mailingwork/Processor/Greenpeace/ClicksTest.php index d175cc7..49ee14a 100644 --- a/tests/phpunit/CRM/Mailingwork/Processor/Greenpeace/ClicksTest.php +++ b/tests/phpunit/CRM/Mailingwork/Processor/Greenpeace/ClicksTest.php @@ -52,7 +52,7 @@ public function setUpHeadless() { ->apply(TRUE); } - public function setUp() { + public function setUp(): void { parent::setUp(); self::createRequiredOptionGroups(); @@ -62,7 +62,7 @@ public function setUp() { $this->createMailingActivities(); } - public function tearDown() { + public function tearDown(): void { CRM_Mailingwork_Identitytracker_Configuration::resetInstance(); parent::tearDown(); @@ -73,16 +73,16 @@ public function testImportClicks() { // -- Define test entities -- // $interests = [ - 'energy' => [ + 'energy' => [ 'id' => 1, 'name' => 'Campaign Energy', ], - 'ocean' => [ + 'ocean' => [ 'id' => 2, 'name' => 'Campaign Ocean', ], ]; - + $links = [ 'gp_energy' => [ 'id' => 1, diff --git a/tests/phpunit/CRM/Mailingwork/Processor/Greenpeace/FoldersTest.php b/tests/phpunit/CRM/Mailingwork/Processor/Greenpeace/FoldersTest.php index 03e6ace..8554dc2 100644 --- a/tests/phpunit/CRM/Mailingwork/Processor/Greenpeace/FoldersTest.php +++ b/tests/phpunit/CRM/Mailingwork/Processor/Greenpeace/FoldersTest.php @@ -23,11 +23,11 @@ public function setUpHeadless() { ->apply(); } - public function setUp() { + public function setUp(): void { parent::setUp(); } - public function tearDown() { + public function tearDown(): void { parent::tearDown(); } diff --git a/tests/phpunit/CRM/Mailingwork/Processor/Greenpeace/MailingsTest.php b/tests/phpunit/CRM/Mailingwork/Processor/Greenpeace/MailingsTest.php index 405febf..01c808e 100644 --- a/tests/phpunit/CRM/Mailingwork/Processor/Greenpeace/MailingsTest.php +++ b/tests/phpunit/CRM/Mailingwork/Processor/Greenpeace/MailingsTest.php @@ -23,11 +23,11 @@ public function setUpHeadless() { ->apply(); } - public function setUp() { + public function setUp(): void { parent::setUp(); } - public function tearDown() { + public function tearDown(): void { parent::tearDown(); } diff --git a/tests/phpunit/CRM/Mailingwork/Processor/Greenpeace/OptoutsTest.php b/tests/phpunit/CRM/Mailingwork/Processor/Greenpeace/OptoutsTest.php index c2f16bd..8e22b64 100644 --- a/tests/phpunit/CRM/Mailingwork/Processor/Greenpeace/OptoutsTest.php +++ b/tests/phpunit/CRM/Mailingwork/Processor/Greenpeace/OptoutsTest.php @@ -48,7 +48,7 @@ public function setUpHeadless() { ->apply(TRUE); } - public function setUp() { + public function setUp(): void { $session = CRM_Core_Session::singleton(); $session->set('userID', 1); $this->setUpFieldsAndData(); @@ -224,7 +224,7 @@ protected function setUpFieldsAndData() { ]); } - public function tearDown() { + public function tearDown(): void { parent::tearDown(); } diff --git a/tests/phpunit/CRM/Mailingwork/Processor/Greenpeace/RetainedEmailsTest.php b/tests/phpunit/CRM/Mailingwork/Processor/Greenpeace/RetainedEmailsTest.php index 395501c..d9b4be9 100644 --- a/tests/phpunit/CRM/Mailingwork/Processor/Greenpeace/RetainedEmailsTest.php +++ b/tests/phpunit/CRM/Mailingwork/Processor/Greenpeace/RetainedEmailsTest.php @@ -23,7 +23,7 @@ public function setUpHeadless() { ->apply(); } - public function setUp() { + public function setUp(): void { $session = CRM_Core_Session::singleton(); $session->set('userID', 1); $this->callApiSuccess('OptionValue', 'create', [ @@ -35,7 +35,7 @@ public function setUp() { parent::setUp(); } - public function tearDown() { + public function tearDown(): void { parent::tearDown(); }