MessageTemplate API - Fix saving of templates with workflow_name sans workflow_id
authorTim Otten <totten@civicrm.org>
Fri, 1 Oct 2021 21:56:26 +0000 (14:56 -0700)
committerTim Otten <totten@civicrm.org>
Fri, 1 Oct 2021 21:56:26 +0000 (14:56 -0700)
Overview
--------

This fixes a bug when saving (updating) a `MessageTemplate` record that
involves a `workflow_name` and no `workflow_id`. It is an alternative to #21674.

Steps to  reproduce
-------------------

Suppose you read a MessagTemplate with values:

```php
$values = ['workflow_name' => 'foo', 'workflow_id' => NULL, ...]
```

Then you save it back:

```php
civicrm_api4('MessageTemplate', 'update', [
  'values' => $values
]);
```

Before
------

The `update` raises an exception.

After
-----

The `update` works.

CRM/Core/BAO/MessageTemplate.php
tests/phpunit/api/v4/Entity/MessageTemplateTest.php [new file with mode: 0644]

index c8d6693a87b7e102f518e50604a06a396803c8b7..7d7d239f131a1815de2cc27771c03f141ea02bce 100644 (file)
@@ -111,7 +111,10 @@ class CRM_Core_BAO_MessageTemplate extends CRM_Core_DAO_MessageTemplate {
     }
 
     // The workflow_id and workflow_name should be sync'd. But what mix of inputs do we have to work with?
-    switch ((empty($params['workflow_id']) ? '' : 'id') . (empty($params['workflow_name']) ? '' : 'name')) {
+    $empty = function ($key) use (&$params) {
+      return empty($params[$key]) || $params[$key] === 'null';
+    };
+    switch (($empty('workflow_id') ? '' : 'id') . ($empty('workflow_name') ? '' : 'name')) {
       case 'id':
         $params['workflow_name'] = array_search($params['workflow_id'], self::getWorkflowNameIdMap());
         break;
diff --git a/tests/phpunit/api/v4/Entity/MessageTemplateTest.php b/tests/phpunit/api/v4/Entity/MessageTemplateTest.php
new file mode 100644 (file)
index 0000000..485865b
--- /dev/null
@@ -0,0 +1,119 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC https://civicrm.org/licensing
+ */
+
+namespace api\v4\Entity;
+
+use api\v4\UnitTestCase;
+use Civi\Test\DbTestTrait;
+use Civi\Test\GenericAssertionsTrait;
+use Civi\Test\TransactionalInterface;
+
+/**
+ * @group headless
+ */
+class MessageTemplateTest extends UnitTestCase implements TransactionalInterface {
+
+  use GenericAssertionsTrait;
+  use DbTestTrait;
+
+  private $baseTpl = [
+    'msg_title' => 'My Template',
+    'msg_subject' => 'My Subject',
+    'msg_text' => 'My body as text',
+    'msg_html' => '<p>My body as HTML</p>',
+    'is_reserved' => TRUE,
+    'is_default' => FALSE,
+  ];
+
+  /**
+   * Create/update a MessageTemplate with workflow_name and no corresponding workflow_id.
+   */
+  public function testWorkflowName_clean() {
+    $create = civicrm_api4('MessageTemplate', 'create', [
+      'values' => $this->baseTpl + ['workflow_name' => 'first', 'workflow_id' => NULL],
+    ])->single();
+    $this->assertDBQuery('first', 'SELECT workflow_name FROM civicrm_msg_template WHERE id = %1', [1 => [$create['id'], 'Int']]);
+    $this->assertDBQuery(NULL, 'SELECT workflow_id FROM civicrm_msg_template WHERE id = %1', [1 => [$create['id'], 'Int']]);
+
+    civicrm_api4('MessageTemplate', 'update', [
+      'where' => [['id', '=', $create['id']]],
+      'values' => ['workflow_name' => 'second', 'workflow_id' => NULL],
+    ])->single();
+    $this->assertDBQuery('second', 'SELECT workflow_name FROM civicrm_msg_template WHERE id = %1', [1 => [$create['id'], 'Int']]);
+    $this->assertDBQuery(NULL, 'SELECT workflow_id FROM civicrm_msg_template WHERE id = %1', [1 => [$create['id'], 'Int']]);
+  }
+
+  /**
+   * Create/update a MessageTemplate with workflow_name - a name which happens to have an older/corresponding workflow_id.
+   */
+  public function testWorkflowName_legacyMatch() {
+    [$firstId, $secondId] = $this->createFirstSecond();
+
+    $create = civicrm_api4('MessageTemplate', 'create', [
+      'values' => $this->baseTpl + ['workflow_name' => 'first', 'workflow_id' => NULL],
+    ])->single();
+    $this->assertDBQuery('first', 'SELECT workflow_name FROM civicrm_msg_template WHERE id = %1', [1 => [$create['id'], 'Int']]);
+    $this->assertDBQuery($firstId, 'SELECT workflow_id FROM civicrm_msg_template WHERE id = %1', [1 => [$create['id'], 'Int']]);
+
+    civicrm_api4('MessageTemplate', 'update', [
+      'where' => [['id', '=', $create['id']]],
+      'values' => ['workflow_name' => 'second', 'workflow_id' => NULL],
+    ])->single();
+    $this->assertDBQuery('second', 'SELECT workflow_name FROM civicrm_msg_template WHERE id = %1', [1 => [$create['id'], 'Int']]);
+    $this->assertDBQuery($secondId, 'SELECT workflow_id FROM civicrm_msg_template WHERE id = %1', [1 => [$create['id'], 'Int']]);
+  }
+
+  /**
+   * Create/update a MessageTempalte with workflow_id. Ensure the newer workflow_name is set.
+   */
+  public function testWorkflowId_legacyMatch() {
+    [$firstId, $secondId] = $this->createFirstSecond();
+
+    $create = civicrm_api4('MessageTemplate', 'create', [
+      'values' => $this->baseTpl + ['workflow_id' => $firstId, 'workflow_name' => NULL],
+    ])->single();
+    $this->assertDBQuery('first', 'SELECT workflow_name FROM civicrm_msg_template WHERE id = %1', [1 => [$create['id'], 'Int']]);
+    $this->assertDBQuery($firstId, 'SELECT workflow_id FROM civicrm_msg_template WHERE id = %1', [1 => [$create['id'], 'Int']]);
+
+    civicrm_api4('MessageTemplate', 'update', [
+      'where' => [['id', '=', $create['id']]],
+      'values' => ['workflow_id' => $secondId, 'workflow_name' => NULL],
+    ])->single();
+    $this->assertDBQuery('second', 'SELECT workflow_name FROM civicrm_msg_template WHERE id = %1', [1 => [$create['id'], 'Int']]);
+    $this->assertDBQuery($secondId, 'SELECT workflow_id FROM civicrm_msg_template WHERE id = %1', [1 => [$create['id'], 'Int']]);
+  }
+
+  protected function createFirstSecond() {
+    $first = civicrm_api4('OptionValue', 'create', [
+      'values' => [
+        'option_group_id:name' => 'msg_tpl_workflow_meta',
+        'label' => 'First',
+        'name' => 'first',
+      ],
+    ]);
+    $second = civicrm_api4('OptionValue', 'create', [
+      'values' => [
+        'option_group_id:name' => 'msg_tpl_workflow_meta',
+        'label' => 'Second',
+        'name' => 'second',
+      ],
+    ]);
+    return [$first->single()['id'], $second->single()['id']];
+  }
+
+}