Merge pull request #23939 from civicrm/5.51
[civicrm-core.git] / CRM / Core / BAO / UserJob.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
10 */
11
12 /**
13 *
14 * @package CRM
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
16 */
17
18 use Civi\Core\ClassScanner;
19 use Civi\UserJob\UserJobInterface;
20
21 /**
22 * This class contains user jobs functionality.
23 */
24 class CRM_Core_BAO_UserJob extends CRM_Core_DAO_UserJob implements \Civi\Core\HookInterface {
25
26 /**
27 * Check on the status of a queue.
28 *
29 * Queues that are attached to a UserJob are necessarily finite - so we can mark them 'completed'
30 * when the task-list reaches empty.
31 *
32 * Note that this runs after processing *every item* in *every queue* (foreground, background,
33 * import, upgrade, ad nauseum). The capacity to handle heavy tasks here is subjective (depending
34 * on the specific queue/use-case). We try to be conservative about I/O until we know that
35 * we're in a suitable context.
36 */
37 public static function on_civi_queue_check(\Civi\Core\Event\GenericHookEvent $e) {
38 /** @var \CRM_Queue_Queue $queue */
39 $queue = $e->queue;
40 $userJobId = static::findUserJobId($queue->getName());
41 if ($userJobId && $queue->numberOfItems() < 1) {
42 $queue->setStatus('completed');
43 }
44 }
45
46 /**
47 * If the `civicrm_queue` changes status, then the `civicrm_user_job` should also change status.
48 *
49 * @param \CRM_Queue_Queue $queue
50 * @param string $status
51 * @throws \API_Exception
52 * @throws \Civi\API\Exception\UnauthorizedException
53 * @see \CRM_Utils_Hook::queueStatus()
54 */
55 public static function hook_civicrm_queueStatus(CRM_Queue_Queue $queue, string $status) {
56 $userJobId = static::findUserJobId($queue->getName());
57 if ($userJobId && $status === 'completed') {
58 \Civi\Api4\UserJob::update()
59 ->addWhere('id', '=', $userJobId)
60 ->setValues(['status_id' => 1])
61 ->execute();
62 }
63 }
64
65 private static function findUserJobId(string $queueName): ?int {
66 if (CRM_Core_Config::isUpgradeMode()) {
67 return NULL;
68 }
69
70 $key = 'userJobId_' . $queueName;
71 if (!isset(Civi::$statics[__CLASS__][$key])) {
72 // Part of the primary structure/purpose of the queue. Shouldn't change.
73 $userJobId = CRM_Core_DAO::singleValueQuery('
74 SELECT uj.id FROM civicrm_queue q
75 INNER JOIN civicrm_user_job uj ON q.id = uj.queue_id
76 WHERE q.name = %1
77 ', [
78 1 => [$queueName, 'String'],
79 ]);
80 Civi::$statics[__CLASS__][$key] = $userJobId;
81 }
82 return Civi::$statics[__CLASS__][$key];
83 }
84
85 /**
86 * Restrict access to the relevant user.
87 *
88 * Note that it is likely we might want to permit other users such as
89 * sysadmins to access other people's user_jobs in future but it has been
90 * kept tightly restricted for initial simplicity (ie do we want to
91 * use an existing permission? a new permission ? do they require
92 * 'view all contacts' etc.
93 *
94 * @inheritDoc
95 */
96 public function addSelectWhereClause(): array {
97 $clauses = [];
98 if (!\CRM_Core_Permission::check('administer queues')) {
99 $clauses['created_id'] = '= ' . (int) CRM_Core_Session::getLoggedInContactID();
100 }
101 CRM_Utils_Hook::selectWhereClause($this, $clauses);
102 return $clauses;
103 }
104
105 /**
106 * Get the statuses for Import Jobs.
107 *
108 * @return array
109 */
110 public static function getStatuses(): array {
111 return [
112 [
113 'id' => 1,
114 'name' => 'completed',
115 'label' => ts('Completed'),
116 ],
117 [
118 'id' => 2,
119 'name' => 'draft',
120 'label' => ts('Draft'),
121 ],
122 [
123 'id' => 3,
124 'name' => 'scheduled',
125 'label' => ts('Scheduled'),
126 ],
127 [
128 'id' => 4,
129 'name' => 'in_progress',
130 'label' => ts('In Progress'),
131 ],
132 ];
133 }
134
135 /**
136 * Get the types Import Jobs.
137 *
138 * This is largely a placeholder at this stage. It will likely wind
139 * up as an option value so extensions can add different types.
140 *
141 * However, for now it just holds the one type being worked on.
142 *
143 * @return array
144 */
145 public static function getTypes(): array {
146 $types = Civi::cache()->get('UserJobTypes');
147 if ($types === NULL) {
148 $types = [];
149 $classes = ClassScanner::get(['interface' => UserJobInterface::class]);
150 foreach ($classes as $class) {
151 $declaredTypes = $class::getUserJobInfo();
152 foreach ($declaredTypes as $index => $declaredType) {
153 $declaredTypes[$index]['class'] = $class;
154 }
155 $types = array_merge($types, $declaredTypes);
156 }
157 Civi::cache()->set('UserJobTypes', $types);
158 }
159 return $types;
160 }
161
162 }