diff --git a/CRM/Core/DAO/UserJob.php b/CRM/Core/DAO/UserJob.php
index ebe7e1ee75f1..cfa405500408 100644
--- a/CRM/Core/DAO/UserJob.php
+++ b/CRM/Core/DAO/UserJob.php
@@ -6,7 +6,7 @@
*
* Generated from xml/schema/CRM/Core/UserJob.xml
* DO NOT EDIT. Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:58e6fcaa028d157bfa074a073111239d)
+ * (GenCodeChecksum:fc2efbbf7b26018fe3dff0cda44ffa28)
*/
/**
@@ -125,6 +125,15 @@ class CRM_Core_DAO_UserJob extends CRM_Core_DAO {
*/
public $metadata;
+ /**
+ * Is this a template configuration (for use by other/future jobs)?
+ *
+ * @var bool|string
+ * (SQL type: tinyint)
+ * Note that values will be retrieved from the database as a string.
+ */
+ public $is_template;
+
/**
* Class constructor.
*/
@@ -350,6 +359,24 @@ public static function &fields() {
'serialize' => self::SERIALIZE_JSON,
'add' => '5.50',
],
+ 'is_template' => [
+ 'name' => 'is_template',
+ 'type' => CRM_Utils_Type::T_BOOLEAN,
+ 'title' => ts('Is Template'),
+ 'description' => ts('Is this a template configuration (for use by other/future jobs)?'),
+ 'required' => TRUE,
+ 'where' => 'civicrm_user_job.is_template',
+ 'default' => '0',
+ 'table_name' => 'civicrm_user_job',
+ 'entity' => 'UserJob',
+ 'bao' => 'CRM_Core_BAO_UserJob',
+ 'localizable' => 0,
+ 'html' => [
+ 'type' => 'CheckBox',
+ 'label' => ts("Is Template"),
+ ],
+ 'add' => '5.51',
+ ],
];
CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']);
}
diff --git a/CRM/Queue/DAO/Queue.php b/CRM/Queue/DAO/Queue.php
index 48debc8284f4..65b349c53e60 100644
--- a/CRM/Queue/DAO/Queue.php
+++ b/CRM/Queue/DAO/Queue.php
@@ -6,7 +6,7 @@
*
* Generated from xml/schema/CRM/Queue/Queue.xml
* DO NOT EDIT. Generated by CRM_Core_CodeGen
- * (GenCodeChecksum:0b068d0a6ba5d6348f11706b3854feb1)
+ * (GenCodeChecksum:85012d3cc88cad6b1c868665e0b490c3)
*/
/**
@@ -118,6 +118,15 @@ class CRM_Queue_DAO_Queue extends CRM_Core_DAO {
*/
public $error;
+ /**
+ * Is this a template configuration (for use by other/future queues)?
+ *
+ * @var bool|string
+ * (SQL type: tinyint)
+ * Note that values will be retrieved from the database as a string.
+ */
+ public $is_template;
+
/**
* Class constructor.
*/
@@ -327,6 +336,24 @@ public static function &fields() {
],
'add' => '5.51',
],
+ 'is_template' => [
+ 'name' => 'is_template',
+ 'type' => CRM_Utils_Type::T_BOOLEAN,
+ 'title' => ts('Is Template'),
+ 'description' => ts('Is this a template configuration (for use by other/future queues)?'),
+ 'required' => TRUE,
+ 'where' => 'civicrm_queue.is_template',
+ 'default' => '0',
+ 'table_name' => 'civicrm_queue',
+ 'entity' => 'Queue',
+ 'bao' => 'CRM_Queue_BAO_Queue',
+ 'localizable' => 0,
+ 'html' => [
+ 'type' => 'CheckBox',
+ 'label' => ts("Is Template"),
+ ],
+ 'add' => '5.51',
+ ],
];
CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']);
}
diff --git a/CRM/Queue/Service.php b/CRM/Queue/Service.php
index 0fd874a96b63..338c46eac253 100644
--- a/CRM/Queue/Service.php
+++ b/CRM/Queue/Service.php
@@ -142,6 +142,12 @@ protected function findCreateQueueSpec(array $queueSpec): array {
return $loaded;
}
+ if (isset($queueSpec['template'])) {
+ $base = $this->findQueueSpec(['name' => $queueSpec['template']]);
+ $reset = ['is_template' => 0];
+ $queueSpec = array_merge($base, $reset, $queueSpec);
+ }
+
$this->validateQueueSpec($queueSpec);
$dao = new CRM_Queue_DAO_Queue();
diff --git a/CRM/Upgrade/Incremental/php/FiveFiftyOne.php b/CRM/Upgrade/Incremental/php/FiveFiftyOne.php
index 4f440dfb01d1..f8ae125bde98 100644
--- a/CRM/Upgrade/Incremental/php/FiveFiftyOne.php
+++ b/CRM/Upgrade/Incremental/php/FiveFiftyOne.php
@@ -36,6 +36,10 @@ public function upgrade_5_51_alpha1($rev): void {
'status', "varchar(16) NULL DEFAULT 'active' COMMENT 'Execution status'");
$this->addTask('Add column "civicrm_queue.error"', 'addColumn', 'civicrm_queue',
'error', "varchar(16) NULL COMMENT 'Fallback behavior for unhandled errors'");
+ $this->addTask('Add column "civicrm_queue.is_template"', 'addColumn', 'civicrm_queue',
+ 'is_template', "tinyint NOT NULL DEFAULT 0 COMMENT 'Is this a template configuration (for use by other/future queues)?'");
+ $this->addTask('Add column "civicrm_user_job.is_template"', 'addColumn', 'civicrm_user_job',
+ 'is_template', "tinyint NOT NULL DEFAULT 0 COMMENT 'Is this a template configuration (for use by other/future jobs)?'");
$this->addTask('Backfill "civicrm_queue.status" and "civicrm_queue.error")', 'fillQueueColumns');
}
diff --git a/tests/phpunit/CRM/Queue/QueueTest.php b/tests/phpunit/CRM/Queue/QueueTest.php
index 781da94e5f64..e3fe0010aa7f 100644
--- a/tests/phpunit/CRM/Queue/QueueTest.php
+++ b/tests/phpunit/CRM/Queue/QueueTest.php
@@ -107,6 +107,38 @@ public function testStatuses() {
}
}
+ public function testTemplating() {
+ \Civi\Api4\Queue::create()->setValues([
+ 'is_template' => TRUE,
+ 'name' => 'test/template',
+ 'type' => 'SqlParallel',
+ 'runner' => 'task',
+ 'error' => 'delete',
+ ])->execute();
+ $this->assertDBQuery(1, "SELECT is_template FROM civicrm_queue WHERE name = 'test/template'");
+
+ $qActive = Civi::queue('test/my-active', [
+ 'template' => 'test/template',
+ ]);
+ $this->assertEquals('test/my-active', $qActive->getName());
+ $this->assertEquals('SqlParallel', $qActive->getSpec('type'));
+ $this->assertEquals('task', $qActive->getSpec('runner'));
+ $this->assertEquals('delete', $qActive->getSpec('error'));
+ $this->assertDBQuery('active', "SELECT status FROM civicrm_queue WHERE name = 'test/my-active'");
+ $this->assertDBQuery(0, "SELECT is_template FROM civicrm_queue WHERE name = 'test/my-active'");
+
+ $qDraft = Civi::queue('test/my-draft', [
+ 'template' => 'test/template',
+ 'status' => 'draft',
+ ]);
+ $this->assertEquals('test/my-draft', $qDraft->getName());
+ $this->assertEquals('SqlParallel', $qDraft->getSpec('type'));
+ $this->assertEquals('task', $qDraft->getSpec('runner'));
+ $this->assertEquals('delete', $qDraft->getSpec('error'));
+ $this->assertDBQuery('draft', "SELECT status FROM civicrm_queue WHERE name = 'test/my-draft'");
+ $this->assertDBQuery(0, "SELECT is_template FROM civicrm_queue WHERE name = 'test/my-active'");
+ }
+
/**
* Create a few queue items; alternately enqueue and dequeue various
*
diff --git a/xml/schema/Core/UserJob.xml b/xml/schema/Core/UserJob.xml
index 0c8113e0c992..dd01a2e79184 100644
--- a/xml/schema/Core/UserJob.xml
+++ b/xml/schema/Core/UserJob.xml
@@ -153,4 +153,17 @@
JSON
5.50
+
+ is_template
+ Is Template
+ boolean
+ true
+ 0
+ Is this a template configuration (for use by other/future jobs)?
+
+ CheckBox
+
+
+ 5.51
+
diff --git a/xml/schema/Queue/Queue.xml b/xml/schema/Queue/Queue.xml
index 9f91aa5800ec..de777a9c8104 100644
--- a/xml/schema/Queue/Queue.xml
+++ b/xml/schema/Queue/Queue.xml
@@ -154,4 +154,17 @@
CRM_Queue_BAO_Queue::getErrorModes
+
+ is_template
+ Is Template
+ boolean
+ true
+ 0
+ Is this a template configuration (for use by other/future queues)?
+
+ CheckBox
+
+
+ 5.51
+