All Data Structures Functions Variables Pages
MergeTagsToModelAttributesAdapter.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 
37  /*
38  * This class is responsible from converting merge tags to relevant attribute values,
39  * apply any language translations and returning the final value.
40  */
42  {
43  const PROPERTY_NOT_FOUND = "!MERGETAG-TO-ATTR-FAILED";
44 
45  const ERROR_ON_FIRST_INVALID_TAG = 1;
46 
47  const DO_NOT_ERROR_ON_FIRST_INVALID_TAG = 0;
48 
49  const SUPPRESS_INVALID_TAG_ERRORS_REPLACE_WITH_EMPTY = -1;
50 
51  const SUPPRESS_INVALID_TAG_ERRORS_KEEP_TAG = -2;
52 
63  public static function resolveMergeTagsArrayToAttributesFromModel(& $mergeTags, $model,
64  & $invalidTags = array(), $language,
65  $errorOnFirstMissing = self::DO_NOT_ERROR_ON_FIRST_INVALID_TAG,
66  $params = array(),
67  $convertNewLinesToBrForAttributeValues = false)
68  {
69  assert('$language == null || is_string($language)');
70  if ($language == null)
71  {
72  $language = Yii::app()->language;
73  }
74  $resolvedMergeTags = array();
75  foreach ($mergeTags as $mergeTag)
76  {
77  $attributeAccessorString = static::resolveStringToAttributeAccessor($mergeTag);
78  $timeQualifier = static::stripTimeDelimiterAndReturnQualifier($attributeAccessorString);
79  $resolvedValue = static::resolveMergeTagToStandardOrRelatedAttribute(
80  $attributeAccessorString,
81  $model,
82  $language,
83  $timeQualifier,
84  $errorOnFirstMissing,
85  $params);
86  if ($resolvedValue === static::PROPERTY_NOT_FOUND)
87  {
88  if ($errorOnFirstMissing === static::ERROR_ON_FIRST_INVALID_TAG)
89  {
90  return false;
91  }
92  if ($errorOnFirstMissing === static::SUPPRESS_INVALID_TAG_ERRORS_KEEP_TAG)
93  {
94  $resolvedMergeTags[$mergeTag] = MergeTagsUtil::TAG_PREFIX . $mergeTag . MergeTagsUtil::TAG_SUFFIX;
95  }
96  $invalidTags[] = $mergeTag;
97  }
98  else
99  {
100  if ($convertNewLinesToBrForAttributeValues)
101  {
102  $resolvedMergeTags[$mergeTag] = nl2br($resolvedValue);
103  }
104  else
105  {
106  $resolvedMergeTags[$mergeTag] = $resolvedValue;
107  }
108  }
109  }
110  $mergeTags = $resolvedMergeTags;
111  if ($errorOnFirstMissing === static::DO_NOT_ERROR_ON_FIRST_INVALID_TAG)
112  {
113  return (empty($invalidTags));
114  }
115 
116  //$errorOnFirstMissing === SUPPRESS_INVALID_TAG_ERRORS_REPLACE_WITH_EMPTY OR SUPPRESS_INVALID_TAG_ERRORS_KEEP_TAG
117  return true;
118  }
119 
120  protected static function stripTimeDelimiterAndReturnQualifier(& $mergeTag)
121  {
122  $timeDelimiterIndex = strpos($mergeTag, MergeTagsUtil::TIME_DELIMITER);
123  if ($timeDelimiterIndex !== false)
124  {
125  $timeQualifier = substr($mergeTag, 0, $timeDelimiterIndex);
126  $mergeTag = substr($mergeTag, $timeDelimiterIndex + 1);
127  return $timeQualifier;
128  }
129  else
130  {
131  return null;
132  }
133  }
134 
135  protected static function resolveMergeTagToStandardOrRelatedAttribute($attributeAccessorString, $model,
136  $language, $timeQualifier,
137  $errorOnFirstMissing, $params)
138  {
139  $attributeName = strtok($attributeAccessorString, '->');
140  if (SpecialMergeTagsAdapter::isSpecialMergeTag($attributeName, $timeQualifier))
141  {
142  return SpecialMergeTagsAdapter::resolve($attributeName, $model, $errorOnFirstMissing, $params);
143  }
144  else
145  {
146  if (!isset($model))
147  {
148  return static::PROPERTY_NOT_FOUND;
149  }
150  elseif (!method_exists($model, 'isAttribute') || !$model->isAttribute($attributeName))
151  {
152  if ($model instanceof Activity)
153  {
154  $metadata = $model::getMetadata();
155  $activityItemsModelClassNamesData = $metadata['Activity']['activityItemsModelClassNames'];
156  foreach ($model->activityItems as $activityItem)
157  {
158  if (ucfirst($attributeName) == get_class($activityItem))
159  {
160  $attributeAccessorString = str_replace($attributeName . '->', '', $attributeAccessorString);
161  return static::resolveMergeTagToStandardOrRelatedAttribute(
162  $attributeAccessorString,
163  $activityItem,
164  $language,
165  $timeQualifier,
166  $errorOnFirstMissing,
167  $params);
168  }
169  if (get_class($activityItem) == 'Item' && array_search(ucfirst($attributeName), $activityItemsModelClassNamesData) !== false)
170  {
171  try
172  {
173  $modelDerivationPathToItem = RuntimeUtil::getModelDerivationPathToItem(ucfirst($attributeName));
174  $castedDownModel = $activityItem->castDown(array($modelDerivationPathToItem));
175  if (ucfirst($attributeName) == get_class($castedDownModel))
176  {
177  $attributeAccessorString = str_replace($attributeName . '->', '', $attributeAccessorString);
178  return static::resolveMergeTagToStandardOrRelatedAttribute(
179  $attributeAccessorString,
180  $castedDownModel,
181  $language,
182  $timeQualifier,
183  $errorOnFirstMissing,
184  $params);
185  }
186  }
187  catch (NotFoundException $e)
188  {
189  //Do nothing
190  }
191  }
192  unset($activityItemsModelClassNamesData[get_class($activityItem)]);
193  }
194  foreach ($activityItemsModelClassNamesData as $relationModelClassName)
195  {
196  if (ucfirst($attributeName) == $relationModelClassName)
197  {
198  $model = new $relationModelClassName();
199  $attributeAccessorString = str_replace($attributeName . '->', '', $attributeAccessorString);
200  return static::resolveMergeTagToStandardOrRelatedAttribute(
201  $attributeAccessorString,
202  $model,
203  $language,
204  $timeQualifier,
205  $errorOnFirstMissing,
206  $params);
207  }
208  }
209  }
210  return static::PROPERTY_NOT_FOUND;
211  }
212  elseif ($model->$attributeName instanceof CurrencyValue)
213  {
214  $model = $model->$attributeName;
215  if ($attributeName === $attributeAccessorString) // We have name of relation, don't have a property requested, like $object->owner
216  {
217  $attributeAccessorString = null;
218  }
219  else
220  {
221  $attributeAccessorString = str_replace($attributeName . '->', '', $attributeAccessorString);
222  }
223  if (empty($attributeAccessorString))
224  {
225  // If a user specific a relation merge tag but not a property, we assume he meant "value" property.
226  $currencyValueModel = $model;
227  $value = static::getAttributeValue($currencyValueModel, 'value', $timeQualifier);
228  return CLocale::getInstance($language)->getCurrencySymbol($currencyValueModel->currency->code) . $value;
229  // We can't use code below because it converts integer values in flat and also add slashes to '.' in float numbers
230  //return Yii::app()->numberFormatter->formatCurrency($value,
231  // $currencyValueModel->currency->code);
232  }
233 
234  return static::resolveMergeTagToStandardOrRelatedAttribute($attributeAccessorString, $model,
235  $language, $timeQualifier,
236  $errorOnFirstMissing, $params);
237  }
238  elseif ($model->$attributeName instanceof CustomField)
239  {
240  $value = static::getAttributeValue($model->$attributeName, 'value', $timeQualifier);
241  // TODO: @Shoaibi/@Jason: Low: need to apply localizations(Date/time/currency formats, ...) here besides translation
242  if ($value)
243  {
244  $value = Zurmo::t($model::getModuleClassName(), $value, array(), null, $language);
245  }
246  return $value;
247  }
248  elseif ($model->isRelation($attributeName))
249  {
250  $model = $model->$attributeName;
251  if ($attributeName === $attributeAccessorString) // We have name of relation, don't have a property requested, like $object->owner
252  {
253  $attributeAccessorString = null;
254  }
255  else
256  {
257  $attributeAccessorString = str_replace($attributeName . '->', '', $attributeAccessorString);
258  }
259  if (empty($attributeAccessorString))
260  {
261  // If a user specific a relation merge tag but not a property, we assume he meant "value" property.
262  if (empty($timeQualifier))
263  {
264  return strval($model);
265  }
266  else
267  {
268  return static::PROPERTY_NOT_FOUND;
269  }
270  }
271  if ($model instanceof RedBeanModels)
272  {
273  $modelClassName = $model->getModelClassName();
274  if ($attributeAccessorString == lcfirst($modelClassName))
275  {
276  $values = array();
277  foreach ($model as $relatedModel)
278  {
279  $values[] = strval($relatedModel);
280  }
281  return ArrayUtil::stringify($values);
282  }
283  }
284  return static::resolveMergeTagToStandardOrRelatedAttribute($attributeAccessorString, $model,
285  $language, $timeQualifier,
286  $errorOnFirstMissing, $params);
287  }
288  else
289  {
290  $attributeType = ModelAttributeToMixedTypeUtil::getType($model, $attributeName);
291  //We don't have any accessor operator after the attributeName e.g. its the last in list
292  if ($attributeName === $attributeAccessorString)
293  {
294  $content = static::getAttributeValue($model, $attributeName, $timeQualifier);
295  if ($attributeType == 'DateTime')
296  {
297  $content .= ' GMT';
298  }
299  return $content;
300  }
301  else
302  {
303  return static::PROPERTY_NOT_FOUND;
304  }
305  }
306  }
307  }
308 
309  protected static function resolveModelUrlByModel($model)
310  {
311  $modelClassName = get_class($model);
312  $moduleClassName = $modelClassName::getModuleClassName();
313  $moduleId = $moduleClassName::getDirectoryName();
314  return Yii::app()->createAbsoluteUrl('/' . $moduleId . '/default/details/', array('id' => $model->id));
315  }
316 
317  protected static function getAttributeValue($model, $attributeName, $timeQualifier)
318  {
319  if (empty($timeQualifier))
320  {
321  return static::getAttributeCurrentValue($model, $attributeName);
322  }
323  else
324  {
325  return static::getAttributePreviousValue($model, $attributeName);
326  }
327  }
328 
329  protected static function getAttributeCurrentValue($model, $attributeName)
330  {
331  if (isset($model->$attributeName))
332  {
333  return $model->$attributeName;
334  }
335  else
336  {
337  return null;
338  }
339  }
340 
341  protected static function getAttributePreviousValue($model, $attributeName)
342  {
343  if (property_exists($model, 'originalAttributeValues') || $model->isAttribute('originalAttributeValues'))
344  {
345  if (isset($model->originalAttributeValues[$attributeName]))
346  {
347  return $model->originalAttributeValues[$attributeName];
348  }
349  else
350  {
351  if (isset($model->$attributeName))
352  {
353  return $model->$attributeName;
354  }
355  }
356  }
357  else
358  {
359  return static::PROPERTY_NOT_FOUND;
360  }
361  return null;
362  }
363 
364  protected static function resolveStringToAttributeAccessor($string)
365  {
366  return StringUtil::camelize(str_replace(MergeTagsUtil::PROPERTY_DELIMITER, '->', strtolower($string)),
367  false,
368  MergeTagsUtil::CAPITAL_DELIMITER);
369  }
370  }
371 ?>
static stringify($data)
Definition: ArrayUtil.php:102
static getModelDerivationPathToItem($modelClassName)
Definition: RuntimeUtil.php:79
static getType($model, $attributeName)
static resolveMergeTagsArrayToAttributesFromModel(&$mergeTags, $model, &$invalidTags=array(), $language, $errorOnFirstMissing=self::DO_NOT_ERROR_ON_FIRST_INVALID_TAG, $params=array(), $convertNewLinesToBrForAttributeValues=false)
Generated on Fri Feb 21 2020 07:10:33