All Data Structures Functions Variables Pages
WorkflowActionProcessingHelper.php
1 <?php
2  /*********************************************************************************
3  * Zurmo is a customer relationship management program developed by
4  * Zurmo, Inc. Copyright (C) 2017 Zurmo Inc.
5  *
6  * Zurmo is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU Affero General Public License version 3 as published by the
8  * Free Software Foundation with the addition of the following permission added
9  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
10  * IN WHICH THE COPYRIGHT IS OWNED BY ZURMO, ZURMO DISCLAIMS THE WARRANTY
11  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
12  *
13  * Zurmo is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Affero General Public License along with
19  * this program; if not, see http://www.gnu.org/licenses or write to the Free
20  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21  * 02110-1301 USA.
22  *
23  * You can contact Zurmo, Inc. with a mailing address at 27 North Wacker Drive
24  * Suite 370 Chicago, IL 60606. or at email address contact@zurmo.com.
25  *
26  * The interactive user interfaces in original and modified versions
27  * of this program must display Appropriate Legal Notices, as required under
28  * Section 5 of the GNU Affero General Public License version 3.
29  *
30  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
31  * these Appropriate Legal Notices must retain the display of the Zurmo
32  * logo and Zurmo copyright notice. If the display of the logo is not reasonably
33  * feasible for technical reasons, the Appropriate Legal Notices must display the words
34  * "Copyright Zurmo Inc. 2017. All rights reserved".
35  ********************************************************************************/
36 
41  {
45  protected $workflowId;
46 
50  protected $workflowName;
51 
55  protected $action;
56 
60  protected $triggeredModel;
61 
65  protected $triggeredByUser;
66 
70  protected $canSaveTriggeredModel;
71 
83  public function __construct($workflowId, $workflowName, ActionForWorkflowForm $action, RedBeanModel $triggeredModel, User $triggeredByUser,
84  $canSaveTriggeredModel = true)
85  {
86  assert('is_int($workflowId)');
87  assert('is_string($workflowName)');
88  assert('is_bool($canSaveTriggeredModel)');
89  $this->workflowId = $workflowId;
90  $this->workflowName = $workflowName;
91  $this->action = $action;
92  $this->triggeredModel = $triggeredModel;
93  $this->triggeredByUser = $triggeredByUser;
94  $this->canSaveTriggeredModel = $canSaveTriggeredModel;
95  }
96 
97  public function processUpdateSelfAction()
98  {
99  if ($this->action->type == ActionForWorkflowForm::TYPE_UPDATE_SELF)
100  {
101  self::processActionAttributesForActionBeforeSave($this->action, $this->triggeredModel,
102  $this->triggeredByUser, $this->triggeredModel);
103  }
104  elseif ($this->action->type == ActionForWorkflowForm::TYPE_CREATE_COMMENT)
105  {
106  self::processCreateCommentAction();
107  }
108  }
109 
110  public function processNonUpdateSelfAction()
111  {
112  if ($this->action->type == ActionForWorkflowForm::TYPE_UPDATE_RELATED)
113  {
114  self::processUpdateRelatedAction();
115  }
116  elseif ($this->action->type == ActionForWorkflowForm::TYPE_CREATE)
117  {
118  self::processCreateAction();
119  }
120  elseif ($this->action->type == ActionForWorkflowForm::TYPE_CREATE_RELATED)
121  {
122  self::processCreateRelatedAction();
123  }
124  elseif ($this->action->type == ActionForWorkflowForm::TYPE_SUBSCRIBE_TO_LIST)
125  {
126  self::processSubscribeToListAction();
127  }
128  elseif ($this->action->type == ActionForWorkflowForm::TYPE_UNSUBSCRIBE_FROM_LIST)
129  {
130  self::processUnsubscribeFromListAction();
131  }
132  }
133 
142  RedBeanModel $model,
143  User $triggeredByUser,
144  RedBeanModel $triggeredModel,
145  $create = false)
146  {
147  assert('is_bool($create)');
148  $processedAttributes = array();
149  foreach ($action->getActionAttributes() as $attribute => $actionAttribute)
150  {
151  if ($actionAttribute->resolveValueBeforeSave() && $actionAttribute->shouldSetValue)
152  {
153  if (null === $relation = ActionForWorkflowForm::resolveFirstRelationName($attribute))
154  {
155  $resolvedModel = $model;
156  $resolvedAttribute = ActionForWorkflowForm::resolveRealAttributeName($attribute);
157  }
158  else
159  {
160  $resolvedModel = $model->{$relation};
161  $resolvedAttribute = ActionForWorkflowForm::resolveRealAttributeName($attribute);
162  }
163  $adapter = new WorkflowActionProcessingModelAdapter($resolvedModel, $triggeredByUser, $triggeredModel);
164  $actionAttribute->resolveValueAndSetToModel($adapter, $resolvedAttribute);
165  $processedAttributes[] = $attribute;
166  }
167  }
168  if ($create)
169  {
170  foreach ($action->resolveAllActionAttributeFormsAndLabelsAndSort() as $attribute => $actionAttribute)
171  {
172  if (!in_array($attribute, $processedAttributes) && $actionAttribute->resolveValueBeforeSave() &&
173  $actionAttribute->shouldSetNullAlternativeValue())
174  {
175  if (null === $relation = ActionForWorkflowForm::resolveFirstRelationName($attribute))
176  {
177  $resolvedModel = $model;
178  $resolvedAttribute = ActionForWorkflowForm::resolveRealAttributeName($attribute);
179  $adapter = new WorkflowActionProcessingModelAdapter($resolvedModel, $triggeredByUser, $triggeredModel);
180  $actionAttribute->resolveNullAlternativeValueAndSetToModel($adapter, $resolvedAttribute);
181  }
182  }
183  }
184  }
185  }
186 
194  RedBeanModel $model,
195  User $triggeredByUser,
196  RedBeanModel $triggeredModel)
197  {
198  foreach ($action->getActionAttributes() as $attribute => $actionAttribute)
199  {
200  if (!$actionAttribute->resolveValueBeforeSave() && $actionAttribute->shouldSetValue)
201  {
202  if (null === $relation = ActionForWorkflowForm::resolveFirstRelationName($attribute))
203  {
204  $resolvedModel = $model;
205  $resolvedAttribute = ActionForWorkflowForm::resolveRealAttributeName($attribute);
206  }
207  else
208  {
209  $resolvedModel = $model->{$relation};
210  $resolvedAttribute = ActionForWorkflowForm::resolveRealAttributeName($attribute);
211  }
212  $adapter = new WorkflowActionProcessingModelAdapter($resolvedModel, $triggeredByUser, $triggeredModel);
213  $actionAttribute->resolveValueAndSetToModel($adapter, $resolvedAttribute);
214  }
215  }
216  }
217 
218  protected function processUpdateRelatedAction()
219  {
220  if ($this->action->relationFilter != ActionForWorkflowForm::RELATION_FILTER_ALL)
221  {
222  throw new NotSupportedException();
223  }
224  $modelClassName = get_class($this->triggeredModel);
225  if ($this->triggeredModel->isADerivedRelationViaCastedUpModel($this->action->relation) &&
226  $this->triggeredModel->getDerivedRelationType($this->action->relation) == RedBeanModel::MANY_MANY)
227  {
228  foreach (WorkflowUtil::resolveDerivedModels($this->triggeredModel, $this->action->relation) as $relatedModel)
229  {
230  self::processActionAttributesForActionBeforeSave($this->action, $relatedModel, $this->triggeredByUser, $this->triggeredModel);
231  $saved = $relatedModel->save();
232  if (!$saved)
233  {
234  throw new FailedToSaveModelException();
235  }
236  }
237  }
238  elseif ($this->triggeredModel->getInferredRelationModelClassNamesForRelation(
240  {
241  foreach (WorkflowUtil::getInferredModelsByAtrributeAndModel($this->action->relation, $this->triggeredModel) as $relatedModel)
242  {
243  self::processActionAttributesForActionBeforeSave($this->action, $relatedModel, $this->triggeredByUser, $this->triggeredModel);
244  $saved = $relatedModel->save();
245  if (!$saved)
246  {
247  throw new FailedToSaveModelException();
248  }
249  }
250  }
251  elseif ($this->triggeredModel->{$this->action->relation} instanceof RedBeanMutableRelatedModels)
252  {
253  foreach ($this->triggeredModel->{$this->action->relation} as $relatedModel)
254  {
255  self::processActionAttributesForActionBeforeSave($this->action, $relatedModel, $this->triggeredByUser, $this->triggeredModel);
256  $saved = $relatedModel->save();
257  if (!$saved)
258  {
259  throw new FailedToSaveModelException();
260  }
261  }
262  }
263  elseif ($modelClassName::isRelationTypeAHasOneVariant($this->action->relation) &&
264  !$modelClassName::isOwnedRelation($this->action->relation))
265  {
266  $relatedModel = $this->triggeredModel->{$this->action->relation};
267  self::processActionAttributesForActionBeforeSave($this->action, $relatedModel, $this->triggeredByUser, $this->triggeredModel);
268  $saved = $relatedModel->save();
269  if (!$saved)
270  {
271  throw new FailedToSaveModelException();
272  }
273  }
274  else
275  {
276  throw new NotSupportedException();
277  }
278  }
279 
280  protected function processCreateAction()
281  {
282  if ($this->resolveCreateModel($this->triggeredModel, $this->action->relation) && $this->canSaveTriggeredModel)
283  {
284  $saved = $this->triggeredModel->save();
285  if (!$saved)
286  {
287  throw new FailedToSaveModelException();
288  }
289  }
290  }
291 
300  protected function resolveCreateModel(RedBeanModel $model, $relation, & $modelToForgetCache = null)
301 
302  {
303  assert('is_string($relation)');
304  $modelClassName = get_class($model);
305  if ($model->isADerivedRelationViaCastedUpModel($relation) &&
306  $model->getDerivedRelationType($relation) == RedBeanModel::MANY_MANY)
307  {
308  $relationModelClassName = $model->getDerivedRelationModelClassName($relation);
309  $inferredRelationName = $model->getDerivedRelationViaCastedUpModelOpposingRelationName($relation);
310  $newModel = new $relationModelClassName();
311  self::processActionAttributesForActionBeforeSave($this->action, $newModel, $this->triggeredByUser, $this->triggeredModel, true);
312  $newModel->{$inferredRelationName}->add($model);
313  $saved = $newModel->save();
314  if (!$saved)
315  {
316  throw new FailedToSaveModelException();
317  }
318  self::processActionAttributesForActionAfterSave($this->action, $newModel, $this->triggeredByUser, $this->triggeredModel);
319  return false;
320  }
321  elseif ($model->getInferredRelationModelClassNamesForRelation(
323  {
324  $relationModelClassName = ModelRelationsAndAttributesToWorkflowAdapter::
326  $newModel = new $relationModelClassName();
327  self::processActionAttributesForActionBeforeSave($this->action, $newModel, $this->triggeredByUser, $this->triggeredModel, true);
328  $saved = $newModel->save();
329  if (!$saved)
330  {
331  throw new FailedToSaveModelException();
332  }
333  self::processActionAttributesForActionAfterSave($this->action, $newModel, $this->triggeredByUser, $this->triggeredModel);
335  return true;
336  }
337  elseif ($model->$relation instanceof RedBeanMutableRelatedModels)
338  {
339  $relationModelClassName = $model->getRelationModelClassName($relation);
340  $newModel = new $relationModelClassName();
341  self::processActionAttributesForActionBeforeSave($this->action, $newModel, $this->triggeredByUser, $this->triggeredModel, true);
342  $this->resolveOneToManyPostCreateActionSaveModelCache($model, $relation, $newModel);
343  $saved = $newModel->save();
344  if (!$saved)
345  {
346  throw new FailedToSaveModelException();
347  }
348  self::processActionAttributesForActionAfterSave($this->action, $newModel, $this->triggeredByUser, $this->triggeredModel);
349  $model->{$relation}->add($newModel);
350  $modelToForgetCache = $newModel;
351  return true;
352  }
353  elseif ($modelClassName::isRelationTypeAHasOneVariant($relation) &&
354  !$modelClassName::isOwnedRelation($relation))
355  {
356  $relatedModel = $model->{$relation};
357  if ($relatedModel->id > 0)
358  {
359  return;
360  }
361  self::processActionAttributesForActionBeforeSave($this->action, $relatedModel, $this->triggeredByUser, $this->triggeredModel, true);
362  if (!$relatedModel->save())
363  {
364  throw new FailedToSaveModelException();
365  }
366  self::processActionAttributesForActionAfterSave($this->action, $relatedModel, $this->triggeredByUser, $this->triggeredModel);
367  return true;
368  }
369  else
370  {
371  throw new NotSupportedException();
372  }
373  }
374 
382  protected function processCreateRelatedAction()
383  {
384  if ($this->action->relationFilter != ActionForWorkflowForm::RELATION_FILTER_ALL)
385  {
386  throw new NotSupportedException();
387  }
388  $modelClassName = get_class($this->triggeredModel);
389  if ($this->triggeredModel->isADerivedRelationViaCastedUpModel($this->action->relation) &&
390  $this->triggeredModel->getDerivedRelationType($this->action->relation) == RedBeanModel::MANY_MANY)
391  {
392  foreach (WorkflowUtil::resolveDerivedModels($this->triggeredModel, $this->action->relation) as $relatedModel)
393  {
394  if ($this->resolveCreateModel($relatedModel, $this->action->relatedModelRelation))
395  {
396  $saved = $relatedModel->save();
397  if (!$saved)
398  {
399  throw new FailedToSaveModelException();
400  }
401  }
402  }
403  }
404  elseif ($this->triggeredModel->getInferredRelationModelClassNamesForRelation(
406  {
407  foreach (WorkflowUtil::getInferredModelsByAtrributeAndModel($this->action->relation, $this->triggeredModel) as $relatedModel)
408  {
409  if ($this->resolveCreateModel($relatedModel, $this->action->relatedModelRelation))
410  {
411  $saved = $relatedModel->save();
412  if (!$saved)
413  {
414  throw new FailedToSaveModelException();
415  }
416  }
417  }
418  }
419  elseif ($this->triggeredModel->{$this->action->relation} instanceof RedBeanMutableRelatedModels)
420  {
421  foreach ($this->triggeredModel->{$this->action->relation} as $relatedModel)
422  {
423  if ($this->resolveCreateModel($relatedModel, $this->action->relatedModelRelation))
424  {
425  $saved = $relatedModel->save();
426  if (!$saved)
427  {
428  throw new FailedToSaveModelException();
429  }
430  }
431  }
432  }
433  elseif ($modelClassName::isRelationTypeAHasOneVariant($this->action->relation) &&
434  !$modelClassName::isOwnedRelation($this->action->relation))
435  {
436  $relatedModel = $this->triggeredModel->{$this->action->relation};
437  $modelToForgetCache = null;
438  if ($this->resolveCreateModel($relatedModel, $this->action->relatedModelRelation, $modelToForgetCache))
439  {
440  $saved = $relatedModel->save();
441  if (!$saved)
442  {
443  throw new FailedToSaveModelException();
444  }
445  if ($modelToForgetCache instanceof RedBeanModel)
446  {
447  $modelToForgetCache->forget();
448  }
449  }
450  }
451  else
452  {
453  throw new NotSupportedException();
454  }
455  }
456 
457  protected function processSubscribeToListAction()
458  {
459  $actionAttributes = $this->action->getActionAttributes();
460  if (count($actionAttributes) > 1 ||
461  !isset($actionAttributes['marketingList']) ||
462  !$this->triggeredModel instanceof Contact)
463  {
464  throw new NotSupportedException();
465  }
466  $marketingListId = $actionAttributes['marketingList']->value;
467  try
468  {
469  //Does the marketing list still exist
470  $marketingList = MarketingList::getById((int)$marketingListId);
471  $marketingList->addNewMember((int)$this->triggeredModel->id, false);
472  }
473  catch (NotFoundException $e)
474  {
475  $this->logAndNotifyOnMissingMarketingListModel(ActionForWorkflowForm::TYPE_UNSUBSCRIBE_FROM_LIST);
476  }
477  }
478 
488  $precedingRelation, RedBeanModel $model)
489  {
490  if ($precedingModel->$precedingRelation instanceof RedBeanOneToManyRelatedModels)
491  {
492  $relationToUse = null;
493  foreach ($model->getAttributes() as $attributeName => $notUsed)
494  {
495  if ($model->isRelation($attributeName))
496  {
497  if (RedBeanModel::relationLinksToPrecedingRelation(get_class($model), $attributeName,
498  get_class($precedingModel), $precedingRelation))
499  {
500  $relationToUse = $attributeName;
501  break;
502  }
503  }
504  }
505  if ($relationToUse != null)
506  {
507  $model->{$relationToUse} = $precedingModel;
508  }
509  }
510  }
511 
512  protected function processUnsubscribeFromListAction()
513  {
514  $actionAttributes = $this->action->getActionAttributes();
515  if (count($actionAttributes) > 1 ||
516  !isset($actionAttributes['marketingList']) ||
517  !$this->triggeredModel instanceof Contact)
518  {
519  throw new NotSupportedException();
520  }
521  $marketingListId = $actionAttributes['marketingList']->value;
522  $members = MarketingListMember::getByMarketingListIdContactIdAndUnsubscribed($marketingListId,
523  (int)$this->triggeredModel->id,
524  false);
525  if ($members !== false)
526  {
527  $member = $members[0];
528  $member->unsubscribed = true;
529  if (!$member->unrestrictedSave())
530  {
531  throw new FailedToSaveModelException();
532  }
533  }
534  else
535  {
536  try
537  {
538  //Does the marketing list still exist
539  MarketingList::getById((int)$marketingListId);
540  }
541  catch (NotFoundException $e)
542  {
543  $this->logAndNotifyOnMissingMarketingListModel(ActionForWorkflowForm::TYPE_UNSUBSCRIBE_FROM_LIST);
544  }
545  }
546  }
547 
552  protected function processCreateCommentAction()
553  {
554  $actionAttributes = $this->action->getActionAttributes();
555  if (count($actionAttributes) > 1 ||
556  !isset($actionAttributes['comments']) ||
557  !$this->triggeredModel->isRelation('comments') ||
558  $this->triggeredModel->getRelationType('comments') != RedBeanModel::HAS_MANY)
559  {
560  throw new NotSupportedException();
561  }
562 
563  $comment = new Comment();
564  // Needed so we can resolve merge tags
565  $adapter = new WorkflowActionProcessingModelAdapter($comment, $this->triggeredByUser, $this->triggeredModel);
566  $actionAttributes['comments']->resolveValueAndSetToModel($adapter, 'description');
567  $comment->description = $adapter->getModel()->description;
568  if (!$this->triggeredModel->comments->contains($comment))
569  {
570  $this->triggeredModel->comments->add($comment);
571  }
572  }
573 
574  protected function logAndNotifyOnMissingMarketingListModel($type)
575  {
576  assert('$type == ActionForWorkflowForm::TYPE_SUBSCRIBE_TO_LIST ||
577  $type == ActionForWorkflowForm::TYPE_UNSUBSCRIBE_FROM_LIST');
579  {
580  $logContent = "\n Workflow model with id: " . $this->workflowId . ' is malformed. Subscribe action references invalid marketing list';
581  Yii::log($logContent, CLogger::LEVEL_WARNING);
582  }
583  else
584  {
585  $logContent = "\n Workflow model with id: " . $this->workflowId . ' is malformed. Unsubscribe action references invalid marketing list';
586  Yii::log($logContent, CLogger::LEVEL_WARNING);
587  }
588  $message = new NotificationMessage();
589  $message->textContent = "Repair the workflow rule named '" . $this->workflowName . "' because it is referencing an invalid marketing list";
591  NotificationsUtil::submit($message, $rules);
592  }
593  }
594 ?>
static processActionAttributesForActionBeforeSave(ActionForWorkflowForm $action, RedBeanModel $model, User $triggeredByUser, RedBeanModel $triggeredModel, $create=false)
static resolveFirstRelationName($attribute)
static getDerivedRelationViaCastedUpModelOpposingRelationName($relationName)
Definition: BeanModel.php:358
Definition: User.php:37
getAttributes(array $attributeNames=null)
__construct($workflowId, $workflowName, ActionForWorkflowForm $action, RedBeanModel $triggeredModel, User $triggeredByUser, $canSaveTriggeredModel=true)
static resolveDerivedModels(RedBeanModel $model, $relation)
static resolveRealAttributeName($attribute)
static getDerivedRelationType($relationName)
Definition: BeanModel.php:335
resolveOneToManyPostCreateActionSaveModelCache(RedBeanModel $precedingModel, $precedingRelation, RedBeanModel $model)
const MANY_MANY
Definition: BeanModel.php:78
static processActionAttributesForActionAfterSave(ActionForWorkflowForm $action, RedBeanModel $model, User $triggeredByUser, RedBeanModel $triggeredModel)
static getById($id, $modelClassName=null)
static relationLinksToPrecedingRelation($modelClassName, $relation, $precedingModelClassName=null, $precedingRelation=null)
static getDerivedRelationModelClassName($relationName)
Definition: BeanModel.php:347
const HAS_MANY
Definition: BeanModel.php:71
static getInferredModelsByAtrributeAndModel($relation, $model)
resolveCreateModel(RedBeanModel $model, $relation, &$modelToForgetCache=null)
static isRelation($attributeName)
Definition: BeanModel.php:220
static isADerivedRelationViaCastedUpModel($relationName)
Definition: BeanModel.php:320
static submit(NotificationMessage $message, NotificationRules $rules)
Generated on Sun Feb 23 2020 07:10:36