forked from Kajakaran/org.civicoop.civirules
-
Notifications
You must be signed in to change notification settings - Fork 0
/
civirules.php
438 lines (411 loc) · 14.6 KB
/
civirules.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
<?php
require_once 'civirules.civix.php';
if (!interface_exists("\\Psr\\Log\\LoggerInterface")) {
require_once('psr/log/LoggerInterface.php');
}
if (!class_exists("\\Psr\\Log\\LogLevel")) {
require_once('psr/log/LogLevel.php');
}
use CRM_Civirules_ExtensionUtil as E;
/**
* Hook to add the symfony event listeners.
*
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
*
* @throws \CRM_Core_Exception
*/
function civirules_civicrm_container(\Symfony\Component\DependencyInjection\ContainerBuilder $container) {
$container->addCompilerPass(new \Civi\ConfigItems\CiviRulesCompilerPass());
// Add the symfony listeners.
// We need to eventID parameter to prevent overwriting of original data in case we
// have a rule based on edit activity and action to edit a second activity.
// See this PR: https://lab.civicrm.org/extensions/civirules/-/merge_requests/96
$container->findDefinition('dispatcher')
->addMethodCall('addListener', [
'civi.dao.preInsert',
'civirules_trigger_preinsert'
])
->addMethodCall('addListener', [
'civi.dao.postInsert',
'civirules_trigger_postinsert'
])
->addMethodCall('addListener', [
'civi.dao.preUpdate',
'civirules_trigger_preupdate'
])
->addMethodCall('addListener', [
'civi.dao.postUpdate',
'civirules_trigger_postupdate'
])
->addMethodCall('addListener', [
'civi.dao.preDelete',
'civirules_trigger_predelete'
])
->addMethodCall('addListener', [
'civi.dao.postDelete',
'civirules_trigger_postdelete'
]);
}
/**
* Implementation of hook_civicrm_config
*
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_config
*/
function civirules_civicrm_config(&$config) {
_civirules_civix_civicrm_config($config);
}
/**
* Implementation of hook_civicrm_install
*
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_install
*/
function civirules_civicrm_install() {
_civirules_civix_civicrm_install();
}
/**
* Implementation of hook_civicrm_enable
*
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_enable
*/
function civirules_civicrm_enable() {
_civirules_civix_civicrm_enable();
}
/**
* Implementation of hook_civicrm_managed
*
* Generate a list of entities to create/deactivate/delete when this module
* is installed, disabled, uninstalled.
*
* @link http://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_managed
*/
function civirules_civicrm_managed(&$entities) {
// First create a backup because the managed entities are gone
// so the actions and conditions table are first going to be emptied
_civirules_upgrade_to_2x_backup();
// Check triggers, actions and conditions
CRM_Civirules_Utils_Upgrader::insertTriggersFromJson(E::path('sql/triggers.json'));
CRM_Civirules_Utils_Upgrader::insertActionsFromJson(E::path('sql/actions.json'));
CRM_Civirules_Utils_Upgrader::insertConditionsFromJson(E::path('sql/conditions.json'));
}
/**
* Helper function to create a backup if the current schema version is of a 1.x version.
* We need this backup to restore missing actions and rules after upgrading.
*/
function _civirules_upgrade_to_2x_backup() {
// Check schema version
// Schema version 1023 is inserted by a 2x version
// So if the schema version is lower than 1023 we are still on a 1x version.
$schemaVersion = CRM_Core_DAO::singleValueQuery("SELECT schema_version FROM civicrm_extension WHERE `name` = 'CiviRules'");
if ($schemaVersion >= 1023) {
return; // No need for preparing the update.
}
if (!CRM_Core_DAO::checkTableExists('civirule_rule_action_backup')) {
// Backup the current action and condition connected to a civirule
CRM_Core_DAO::executeQuery("
CREATE TABLE `civirule_rule_action_backup`
SELECT `civirule_rule_action`.*, `civirule_action`.`class_name` as `action_class_name`
FROM `civirule_rule_action`
INNER JOIN `civirule_action` ON `civirule_rule_action`.`action_id` = `civirule_action`.`id`
");
}
if (!CRM_Core_DAO::checkTableExists('civirule_rule_action_backup')) {
CRM_Core_DAO::executeQuery("
CREATE TABLE `civirule_rule_condition_backup`
SELECT `civirule_rule_condition`.*, `civirule_condition`.`class_name` as `condition_class_name`
FROM `civirule_rule_condition`
INNER JOIN `civirule_condition` ON `civirule_rule_condition`.`condition_id` = `civirule_condition`.`id`
");
}
}
/**
* By default we use the Symfony event for preInsert, preUpdate, postInsert etc.
* However there a couple of entities which do not work yet with the symfony events.
*
* In the future this should be refactored so that this would be simplified
* so that the inner workings are unambiguously with symfony events but that also depends
* on CiviCRM core changes.
*
* @param $op
* @param $objectName
* @param $objectId
* @param $params
*/
function civirules_civicrm_pre($op, $objectName, $objectId, &$params) {
// New style pre/post Delete/Insert/Update events exist from 5.34.
if (civirules_use_prehook($op, $objectName, $objectId, $params)) {
try {
CRM_Civirules_Utils_PreData::pre($op, $objectName, $objectId, $params, 1);
CRM_Civirules_Utils_CustomDataFromPre::pre($op, $objectName, $objectId, $params, 1);
} catch (\Exception $ex) {
// Do nothing.
}
}
}
/**
* By default we use the Symfony event for preInsert, preUpdate, postInsert etc.
* However there a couple of entities which do not work yet with the symfony events.
*
* In the future this should be refactored so that this would be simplified
* so that the inner workings are unambiguously with symfony events but that also depends
* on CiviCRM core changes.
*
* @param $op
* @param $objectName
* @param $objectId
* @param $objectRef
*/
function civirules_civicrm_post($op, $objectName, $objectId, &$objectRef) {
if (civirules_use_posthook($op, $objectName, $objectId, $objectRef)) {
civirules_instanciate_post_trigger($op, $objectName, $objectId, $objectRef, 1);
}
}
/**
* Function to check whether the pre hook could be called from a symfony
* preInsert, preUpdate, preDelete event. Or whether this should be called from
* the traditional hook_civicrm_pre.
*
* @param $op
* @param $objectName
* @param $objectId
* @param $params
*
* @return bool
*/
function civirules_use_prehook($op, $objectName, $objectId, &$params) {
if ($objectName == 'GroupContact') {
return TRUE;
}
return FALSE;
}
/**
* Function to check whether the post trigger could be called from a symfony
* postInsert, postUpdate, postDelete event. Or whether this should be called from
* the traditional hook_civicrm_post.
*
* @param $op
* @param $objectName
* @param $objectId
* @param $objectRef
*
* @return bool
*/
function civirules_use_posthook($op, $objectName, $objectId, &$objectRef) {
if ($objectName == 'GroupContact' && is_array($objectRef)) {
// GroupContact with the objectRef as an array of contact ids does
// call hook_civicrm_post directly and does not invoke a civicrm event.
return TRUE;
}
return FALSE;
}
/**
* This event is called before an entity is inserted in the database.
*
* @param \Civi\Core\DAO\Event\PreUpdate $event
*/
function civirules_trigger_preinsert(\Civi\Core\DAO\Event\PreUpdate $event) {
try {
$objectName = CRM_Civirules_Utils::getObjectNameFromObject($event->object);
$objectId = $event->object->id;
$eventID = $event->eventID ?? 1;
$params = [];
CRM_Core_DAO::storeValues($event->object, $params);
CRM_Civirules_Utils_PreData::pre('create', $objectName, $objectId, $params, $eventID);
CRM_Civirules_Utils_CustomDataFromPre::pre('create', $objectName, $objectId, $params, $eventID);
} catch (\Exception $ex) {
// Do nothing.
}
}
/**
* This event is called after an entity is inserted in the database.
*
* @param $event
*/
function civirules_trigger_postinsert($event) {
$objectName = CRM_Civirules_Utils::getObjectNameFromObject($event->object);
$eventID = $event->eventID ?? 1;
if (!civirules_use_posthook('create', $objectName, $event->object->id, $event->object)) {
civirules_instanciate_post_trigger('create', $objectName, $event->object->id, $event->object, $eventID);
}
}
/**
* This event is called before an entity is updated in the database.
*
* @param \Civi\Core\DAO\Event\PreUpdate $event
*/
function civirules_trigger_preupdate(\Civi\Core\DAO\Event\PreUpdate $event) {
try {
$objectName = CRM_Civirules_Utils::getObjectNameFromObject($event->object);
$objectId = $event->object->id;
$eventID = $event->eventID ?? 1;
$params = [];
CRM_Core_DAO::storeValues($event->object, $params);
CRM_Civirules_Utils_PreData::pre('edit', $objectName, $objectId, $params, $eventID);
CRM_Civirules_Utils_CustomDataFromPre::pre('edit', $objectName, $objectId, $params, $eventID);
} catch (\Exception $ex) {
// Do nothing.
}
}
/**
* This event is called after an entity is updated in the database.
*
* @param $event
*/
function civirules_trigger_postupdate($event) {
$objectName = CRM_Civirules_Utils::getObjectNameFromObject($event->object);
$eventID = $event->eventID ?? 1;
if (!civirules_use_posthook('edit', $objectName, $event->object->id, $event->object)) {
civirules_instanciate_post_trigger('edit', $objectName, $event->object->id, $event->object, $eventID);
}
}
/**
* This event is called before an entity is deleted.
*
* @param \Civi\Core\DAO\Event\PreDelete $event
*/
function civirules_trigger_predelete(\Civi\Core\DAO\Event\PreDelete $event) {
try {
$objectName = CRM_Civirules_Utils::getObjectNameFromObject($event->object);
$objectId = $event->object->id;
$eventID = $event->eventID ?? 1;
$params = [];
CRM_Core_DAO::storeValues($event->object, $params);
CRM_Civirules_Utils_PreData::pre('delete', $objectName, $objectId, $params, $eventID);
CRM_Civirules_Utils_CustomDataFromPre::pre('delete', $objectName, $objectId, $params, $eventID);
} catch (\Exception $ex) {
// Do nothing.
}
}
/**
* This event is called after an entity is deleted.
*
* @param $event
*/
function civirules_trigger_postdelete($event) {
$objectName = CRM_Civirules_Utils::getObjectNameFromObject($event->object);
$eventID = $event->eventID ?? 1;
if (!civirules_use_posthook('delete', $objectName, $event->object->id, $event->object)) {
civirules_instanciate_post_trigger('delete', $objectName, $event->object->id, $event->object, $eventID);
}
}
/**
* Function to call the post trigger.
* It either creates a callback when a transaction is active or it calls triggers directly.
*
* @param $op
* @param $objectName
* @param $objectId
* @param $objectRef
* @param $eventId
*/
function civirules_instanciate_post_trigger($op, $objectName, $objectId, $objectRef, $eventId) {
try {
if (CRM_Core_Transaction::isActive()) {
CRM_Core_Transaction::addCallback(CRM_Core_Transaction::PHASE_POST_COMMIT, 'civirules_call_post_trigger', [
$op,
$objectName,
$objectId,
$objectRef,
$eventId
]);
}
else {
civirules_call_post_trigger($op, $objectName, $objectId, $objectRef, $eventId);
}
} catch (\Exception $ex) {
// Do nothing.
}
}
/**
* This callback is called from the post hooks or after the transaction is completed.
*
* @param $op
* @param $objectName
* @param $objectId
* @param $objectRef
* @param $eventID
*/
function civirules_call_post_trigger($op, $objectName, $objectId, $objectRef, $eventID) {
try {
CRM_Civirules_Trigger_Post::post($op, $objectName, $objectId, $objectRef, $eventID);
} catch (\Exception $ex) {
// Do nothing.
}
}
/**
* This is the pre hook before custom data has been changed.
*
* @param $op
* @param $groupID
* @param $entityID
* @param $params
*/
function civirules_civicrm_customPre($op, $groupID, $entityID, &$params) {
try {
CRM_Civirules_Utils_PreData::customPre($op, $groupID, $entityID, $params, 1);
} catch (\Exception $ex) {
// Do nothing.
}
}
/**
* This is the post hook after custom data has been changed.
*
* @param $op
* @param $groupID
* @param $entityID
* @param $params
*
* @throws \CRM_Core_Exception
*/
function civirules_civicrm_custom($op, $groupID, $entityID, &$params) {
/**
* Fix/Hack for issue #208 (https://github.com/CiviCooP/org.civicoop.civirules/issues/208)
*
* To reproduce:
* - create a custom data set for contacts that supports multiple records
* - create a rule that triggers on custom data changing
* - add a record to that custom data set for a contact
* - delete the record
* - observe the logs
*
* This returns the error: "Expected one Contact but found 25"
* Traced to CRM/CivirulesPostTrigger/ContactCustomDataChanged.php where there is an api call to contacts getsingle. The issue is that when the custom data record is deleted, there is no remaining entity_id with which to retrieve the contact, and so no id is passed to the getsingle call.
*
* The fix is to check whether the $op is delete and whether $entityID is empty and then check
* whether the contactID is provided in the url.
*/
if ($op == 'delete' && empty($entityID)) {
$contactId = CRM_Utils_Request::retrieve('contactId', 'Positive');
if (!empty($contactId)) {
$entityID = $contactId;
}
}
/** End ugly hack */
CRM_CivirulesPostTrigger_CaseCustomDataChanged::custom($op, $groupID, $entityID, $params);
CRM_CivirulesPostTrigger_ContactCustomDataChanged::custom($op, $groupID, $entityID, $params);
CRM_CivirulesPostTrigger_IndividualCustomDataChanged::custom($op, $groupID, $entityID, $params);
CRM_CivirulesPostTrigger_OrganizationCustomDataChanged::custom($op, $groupID, $entityID, $params);
CRM_CivirulesPostTrigger_HouseholdCustomDataChanged::custom($op, $groupID, $entityID, $params);
}
function civirules_civirules_alter_trigger_data(CRM_Civirules_TriggerData_TriggerData &$triggerData) {
//also add the custom data which is passed to the pre hook (and not the post)
CRM_Civirules_Utils_CustomDataFromPre::addCustomDataToTriggerData($triggerData);
}
/**
* Implements hook_civicrm_apiWrappers()
*
* @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_apiWrappers/
*/
function civirules_civicrm_apiWrappers(&$wrappers, $apiRequest) {
if ($apiRequest['entity'] == 'Contact' && $apiRequest['action'] == 'create') {
$wrappers[] = new CRM_Civirules_TrashRestoreApiWrapper();
}
}
/**
* Implements hook_civicrm_permission().
*/
function civirules_civicrm_permission(&$permissions) {
$permissions['administer CiviRules'] = [
'label' => E::ts('CiviRules: administer CiviRules extension'),
'description' => E::ts('Perform all CiviRules administration tasks in CiviCRM'),
];
}