Account Suspended
Account Suspended
This Account has been suspended.
Contact your hosting provider for more information.
 All Data Structures Functions Variables Pages
Campaign.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 
38  {
39  const STATUS_PAUSED = 1;
40 
41  const STATUS_ACTIVE = 2;
42 
43  const STATUS_PROCESSING = 3;
44 
45  const STATUS_COMPLETED = 4;
46 
47  public static function getByName($name)
48  {
49  return self::getByNameOrEquivalent('name', $name);
50  }
51 
52  public static function getModuleClassName()
53  {
54  return 'CampaignsModule';
55  }
56 
57  public static function getStatusDropDownArray()
58  {
59  return array(
60  static::STATUS_PAUSED => Zurmo::t('CampaignsModule', 'Paused'),
61  static::STATUS_ACTIVE => Zurmo::t('Core', 'Scheduled'),
62  static::STATUS_PROCESSING => Zurmo::t('Core', 'Sending'),
63  static::STATUS_COMPLETED => Zurmo::t('Core', 'Completed'),
64  );
65  }
66 
67  public function __toString()
68  {
69  try
70  {
71  if (trim($this->name) == '')
72  {
73  return Yii::t('Core', '(Unnamed)');
74  }
75  return $this->name;
76  }
78  {
79  return '';
80  }
81  }
82 
88  protected static function getLabel($language = null)
89  {
90  return Zurmo::t('CampaignsModule', 'Campaign', array(), null, $language);
91  }
92 
98  protected static function getPluralLabel($language = null)
99  {
100  return Zurmo::t('CampaignsModule', 'Campaigns', array(), null, $language);
101  }
102 
103  public static function canSaveMetadata()
104  {
105  return true;
106  }
107 
108  public static function isTypeDeletable()
109  {
110  return true;
111  }
112 
113  public static function getByStatus($status, $pageSize = null)
114  {
115  assert('is_int($status)');
116  $searchAttributeData = array();
117  $searchAttributeData['clauses'] = array(
118  1 => array(
119  'attributeName' => 'status',
120  'operatorType' => 'equals',
121  'value' => intval($status),
122  ),
123  );
124  $searchAttributeData['structure'] = '1';
125  $joinTablesAdapter = new RedBeanModelJoinTablesQueryAdapter(get_called_class());
126  $where = RedBeanModelDataProvider::makeWhere(get_called_class(), $searchAttributeData, $joinTablesAdapter);
127  return self::getSubset($joinTablesAdapter, null, $pageSize, $where, null);
128  }
129 
130  public static function getByStatusAndSendingTime($status, $sendingTimestamp = null, $pageSize = null, $offset = null, $inPast = true)
131  {
132  assert('is_int($status)');
133  assert('$offset === null || is_int($offset)');
134  assert('is_bool($inPast)');
135  if (empty($sendingTimestamp))
136  {
137  $sendingTimestamp = time();
138  }
139  $sendOnDateTime = DateTimeUtil::convertTimestampToDbFormatDateTime($sendingTimestamp);
140  if ($inPast)
141  {
142  $sendOnDateTimeOperator = 'lessThan';
143  }
144  else
145  {
146  $sendOnDateTimeOperator = 'greaterThan';
147  }
148  $searchAttributeData = array();
149  $searchAttributeData['clauses'] = array(
150  1 => array(
151  'attributeName' => 'status',
152  'operatorType' => 'equals',
153  'value' => intval($status),
154  ),
155  2 => array(
156  'attributeName' => 'sendOnDateTime',
157  'operatorType' => $sendOnDateTimeOperator,
158  'value' => $sendOnDateTime,
159  ),
160  );
161  $searchAttributeData['structure'] = '(1 and 2)';
162  $joinTablesAdapter = new RedBeanModelJoinTablesQueryAdapter(get_called_class());
163  $where = RedBeanModelDataProvider::makeWhere(get_called_class(), $searchAttributeData, $joinTablesAdapter);
164  return self::getSubset($joinTablesAdapter, $offset, $pageSize, $where, null);
165  }
166 
167  public static function getDefaultMetadata()
168  {
169  $metadata = parent::getDefaultMetadata();
170  $metadata[__CLASS__] = array(
171  'members' => array(
172  'name',
173  'subject',
174  'status',
175  'sendOnDateTime',
176  'supportsRichText',
177  'enableTracking',
178  'htmlContent',
179  'textContent',
180  'fromName',
181  'fromAddress'
182  ),
183  'rules' => array(
184  array('name', 'required'),
185  array('name', 'type', 'type' => 'string'),
186  array('name', 'length', 'min' => 1, 'max' => 64),
187  // putting it on name just so this validator gets executed, other than that there is no binding at all
188  array('name', 'OnlyEditableAttributesAreSetValidator'),
189  array('status', 'required'),
190  array('status', 'type', 'type' => 'integer'),
191  array('status', 'default', 'value' => static::STATUS_ACTIVE),
192  array('supportsRichText', 'required'),
193  array('supportsRichText', 'boolean'),
194  array('sendOnDateTime', 'required'),
195  array('sendOnDateTime', 'type', 'type' => 'datetime'),
196  array('sendOnDateTime', 'dateTimeDefault', 'value' => DateTimeCalculatorUtil::NOW),
197  array('fromName', 'required'),
198  array('fromName', 'type', 'type' => 'string'),
199  array('fromName', 'length', 'min' => 1, 'max' => 64),
200  array('fromAddress', 'required'),
201  array('fromAddress', 'type', 'type' => 'string'),
202  array('fromAddress', 'length', 'min' => 6, 'max' => 64),
203  array('fromAddress', 'email'),
204  array('subject', 'required'),
205  array('subject', 'type', 'type' => 'string'),
206  array('subject', 'length', 'min' => 1, 'max' => 255),
207  array('htmlContent', 'type', 'type' => 'string'),
208  array('textContent', 'type', 'type' => 'string'),
209  array('htmlContent', 'StripDummyHtmlContentFromOtherwiseEmptyFieldValidator'),
210  array('textContent', 'AtLeastOneContentAreaRequiredValidator', 'except' => 'searchModel'),
211  array('htmlContent', 'CampaignMergeTagsValidator', 'except' => 'searchModel'),
212  array('textContent', 'CampaignMergeTagsValidator', 'except' => 'searchModel'),
213  array('enableTracking', 'boolean'),
214  array('enableTracking', 'default', 'value' => false),
215  array('marketingList', 'required')
216  ),
217  'relations' => array(
218  'campaignItems' => array(static::HAS_MANY, 'CampaignItem'),
219  'marketingList' => array(static::HAS_ONE, 'MarketingList', static::NOT_OWNED),
220  'files' => array(static::HAS_MANY, 'FileModel', static::OWNED,
221  static::LINK_TYPE_POLYMORPHIC, 'relatedModel'),
222  ),
223  'elements' => array(
224  'marketingList' => 'MarketingList',
225  'htmlContent' => 'TextArea',
226  'textContent' => 'TextArea',
227  'supportsRichText' => 'CheckBox',
228  'enableTracking' => 'CheckBox',
229  'sendOnDateTime' => 'DateTime',
230  'status' => 'CampaignStatus'
231  ),
232  'defaultSortAttribute' => 'name',
233  );
234  return $metadata;
235  }
236 
237  protected static function translatedAttributeLabels($language)
238  {
239  return array_merge(parent::translatedAttributeLabels($language),
240  array(
241  'name' => Zurmo::t('Core', 'Name', null, null, $language),
242  'status' => Zurmo::t('ZurmoModule', 'Status', null, null, $language),
243  'sendOnDateTime' => Zurmo::t('CampaignsModule', 'Send On', null, null, $language),
244  'supportsRichText' => Zurmo::t('CampaignsModule', 'Supports HTML', null, null, $language),
245  'fromName' => Zurmo::t('EmailMessagesModule', 'From Name', null, null, $language),
246  'fromAddress' => Zurmo::t('EmailMessagesModule', 'From Address', null, null, $language),
247  'subject' => Zurmo::t('Core', 'Subject', null, null, $language),
248  'htmlContent' => Zurmo::t('EmailMessagesModule', 'Html Content', null, null, $language),
249  'textContent' => Zurmo::t('EmailMessagesModule', 'Text Content', null, null, $language),
250  )
251  );
252  }
253 
254  public static function hasReadPermissionsOptimization()
255  {
256  return true;
257  }
258 
259  public static function getGamificationRulesType()
260  {
261  return 'CampaignGamification';
262  }
263 
264  public function getErrors($attributeNameOrNames = null)
265  {
266  // TODO: @Shoaibi/@Jason: Low: We should have overridden getErrors' original code but this was easier.
267  // this was done because marketingList is required but we didn't used to get an error with the right
268  // form with parent's getErrors. We had something like "Name cannot be blank." for MarketingList too
269  // with this we have: "Marketing List cannot be blank."
270  return $this->attributeNameToErrors;
271  }
272 
273  public function beforeValidate()
274  {
275  if ($this->getScenario() != 'searchModel')
276  {
277  $this->validateHtmlOnly();
278  }
279  return parent::beforeValidate();
280  }
281 
282  protected function validateHtmlOnly()
283  {
284  if ($this->supportsRichText && empty($this->htmlContent))
285  {
286  $this->addError('htmlContent', Zurmo::t('CampaignsModule', 'You choose to support HTML but didn\'t set any HTML content.'));
287  return false;
288  }
289  if (!$this->supportsRichText && empty($this->textContent))
290  {
291  $this->addError('textContent', Zurmo::t('CampaignsModule', 'You choose not to support HTML but didn\'t set any text content.'));
292  return false;
293  }
294  return true;
295  }
296 
297  public function togglePausedStatusToActive()
298  {
299  // if this is true by default(install), demo data can't create campaigns that are paused.
300  // tests that expect the status to be left paused would fail too.
301  // we could hack here but using scenarios, is_cli, etc but nothing would be too definite without
302  // making a nice spaghetti here.
303  return false;
304  }
305 
306  protected function beforeSave()
307  {
308  if ($this->togglePausedStatusToActive() && $this->status == static::STATUS_PAUSED)
309  {
310  $this->status = static::STATUS_ACTIVE;
311  }
312  return parent::beforeSave();
313  }
314 
315  protected function afterSave()
316  {
317  $this->deleteCampaignItemsForUnsetEmailMessagesIfPausedToggledToActiveStatus();
318  $resolveForOldModel = false;
319  if (isset($this->originalAttributeValues['status']) && $this->status == static::STATUS_ACTIVE)
320  {
321  $resolveForOldModel = true;
322  }
323 
324  Yii::app()->jobQueue->resolveToAddJobTypeByModelByDateTimeAttribute($this, 'sendOnDateTime',
325  'CampaignGenerateDueCampaignItems', $resolveForOldModel);
326  parent::afterSave();
327  }
328 
329  protected function afterDelete()
330  {
331  foreach ($this->campaignItems as $item)
332  {
333  $item->delete();
334  }
335  $campaignEmailMessageContent = CampaignEmailMessageContent::getByCampaignId($this->id);
336  if (is_object($campaignEmailMessageContent))
337  {
338  $campaignEmailMessageContent->delete();
339  }
340  return parent::afterDelete();
341  }
342 
343  protected function deleteCampaignItemsForUnsetEmailMessagesIfPausedToggledToActiveStatus()
344  {
345  if (!isset($this->originalAttributeValues['status']) ||
346  $this->originalAttributeValues['status'] != static::STATUS_PAUSED)
347  {
348  return;
349  }
350  $modifiedAttributeKeys = array_keys(array_filter($this->originalAttributeValues));
351  $dependentAttributesModified = array_diff($modifiedAttributeKeys, array('name', 'status'));
352  $purgeUnsentCampaignItems = (!empty($dependentAttributesModified));
353  if ($purgeUnsentCampaignItems)
354  {
355  $this->deleteUnprocessedCampaignItems();
356  $unsetEmailMessagesForCurrentCampaign = EmailMessage::getByFolderTypeAndCampaignId(EmailFolder::TYPE_OUTBOX, $this->id);
357  foreach ($unsetEmailMessagesForCurrentCampaign as $emailMessage)
358  {
359  // deleting campaign item should automatically delete any associated data.
360  $emailMessage->campaignItem->delete();
361  }
362  }
363  }
364 
365  protected function deleteUnprocessedCampaignItems()
366  {
367  $campaignitems = CampaignItem::getByProcessedAndCampaignId(0, $this->id);
368  foreach ($campaignitems as $campaignitem)
369  {
370  $campaignitem->delete();
371  }
372  }
373 
374  public function isAttributeEditable($attributeName)
375  {
376  $editableAttributes = $this->getEditableAttributes();
377  return in_array($attributeName, $editableAttributes);
378  }
379 
380  public function getEditableAttributes()
381  {
382  // isNewModel check fails sometimes here, better to check for id < 0
383  if ($this->id < 0 || $this->status == static::STATUS_ACTIVE)
384  {
385  return $this->getEditableAttributesForNewOrActiveStatus();
386  }
387 
388  if ($this->status == static::STATUS_PROCESSING || $this->status == static::STATUS_COMPLETED)
389  {
390  return $this->getEditableAttributesForProcessingOrCompletedStatus();
391  }
392 
393  if ($this->status == static::STATUS_PAUSED)
394  {
395  return $this->getEditableAttributesForPausedStatus();
396  }
397  throw new Exception("Unable to determine editable attributes for id#" . $this->id . ' and status: ' . $this->status);
398  }
399 
400  public function getEditableAttributesForNewOrActiveStatus()
401  {
402  $members = static::getMemberAttributes();
403  $specialMembers = array('marketingList');
404  $specialElements = array('EmailTemplate', 'Files', 'owner');
405  $allowedAttributes = CMap::mergeArray($members, $specialMembers, $specialElements);
406  return $allowedAttributes;
407  }
408 
409  public function getEditableAttributesForProcessingOrCompletedStatus()
410  {
411  $allowedAttributes = array('name');
412  // either the current status should not be completed(e.g. processing, as that is the only status under
413  // which this function would be called)
414  // or we should have moved from a processing status(to completed, as that is the only status under
415  // which this function would be called)
416  if ($this->status != static::STATUS_COMPLETED ||
417  (isset($this->originalAttributeValues['status']) &&
418  $this->originalAttributeValues['status'] == static::STATUS_PROCESSING))
419  {
420  $allowedAttributes[] = 'status';
421  }
422  return $allowedAttributes;
423  }
424 
425  public function getEditableAttributesForPausedStatus()
426  {
427  $members = static::getMemberAttributes();
428  $specialElements = array('EmailTemplate', 'Files', 'owner');
429  $allowedAttributes = CMap::mergeArray($members, $specialElements);
430  return $allowedAttributes;
431  }
432 
433  public function getMemberAttributes()
434  {
435  $metadata = static::getMetadata();
436  $members = $metadata[get_class($this)]['members'];
437  return $members;
438  }
439  }
440 ?>
static getByProcessedAndCampaignId($processed, $campaignId, $pageSize=null)
static makeWhere($modelClassName, array $metadata, &$joinTablesAdapter)
static getLabel($language=null)
Definition: Campaign.php:88
addError($attributeName, $errorMessage)
static getPluralLabel($language=null)
Definition: Campaign.php:98
Generated on Thu Jul 2 2020 07:10:31
Account Suspended
Account Suspended
This Account has been suspended.
Contact your hosting provider for more information.