protected static function resolveMergeTagToStandardOrRelatedAttribute($attributeAccessorString, $model, $language, $timeQualifier)
 {
     $attributeName = strtok($attributeAccessorString, '->');
     if (SpecialMergeTagsAdapter::isSpecialMergeTag($attributeName, $timeQualifier)) {
         return SpecialMergeTagsAdapter::resolve($attributeName, $model);
     } else {
         if (!$model->isAttribute($attributeName)) {
             return static::PROPERTY_NOT_FOUND;
         } elseif ($model->{$attributeName} instanceof CustomField) {
             $value = static::getAttributeValue($model->{$attributeName}, 'value', $timeQualifier);
             // TODO: @Shoaibi/@Jason: Low: need to apply localizations(Date/time/currency formats, ...) here besides translation
             if ($value) {
                 $value = Zurmo::t($model::getModuleClassName(), $value, array(), null, $language);
             }
             return $value;
         } elseif ($model->isRelation($attributeName)) {
             $model = $model->{$attributeName};
             if ($attributeName === $attributeAccessorString) {
                 $attributeAccessorString = null;
             } else {
                 $attributeAccessorString = str_replace($attributeName . '->', '', $attributeAccessorString);
             }
             if (empty($attributeAccessorString)) {
                 // If a user specific a relation merge tag but not a property, we assume he meant "value" property.
                 if (empty($timeQualifier)) {
                     return strval($model);
                 } else {
                     return static::PROPERTY_NOT_FOUND;
                 }
             }
             return static::resolveMergeTagToStandardOrRelatedAttribute($attributeAccessorString, $model, $language, $timeQualifier);
         } else {
             if ($attributeName === $attributeAccessorString) {
                 return static::getAttributeValue($model, $attributeName, $timeQualifier);
             } else {
                 return static::PROPERTY_NOT_FOUND;
             }
         }
     }
 }
 protected static function resolveMergeTagToStandardOrRelatedAttribute($attributeAccessorString, $model, $language, $timeQualifier, $errorOnFirstMissing, $params)
 {
     $attributeName = strtok($attributeAccessorString, '->');
     if (SpecialMergeTagsAdapter::isSpecialMergeTag($attributeName, $timeQualifier)) {
         return SpecialMergeTagsAdapter::resolve($attributeName, $model, $errorOnFirstMissing, $params);
     } else {
         if (!isset($model)) {
             return static::PROPERTY_NOT_FOUND;
         } elseif (!method_exists($model, 'isAttribute') || !$model->isAttribute($attributeName)) {
             if ($model instanceof Activity) {
                 $metadata = $model::getMetadata();
                 $activityItemsModelClassNamesData = $metadata['Activity']['activityItemsModelClassNames'];
                 foreach ($model->activityItems as $activityItem) {
                     if (ucfirst($attributeName) == get_class($activityItem)) {
                         $attributeAccessorString = str_replace($attributeName . '->', '', $attributeAccessorString);
                         return static::resolveMergeTagToStandardOrRelatedAttribute($attributeAccessorString, $activityItem, $language, $timeQualifier, $errorOnFirstMissing, $params);
                     }
                     if (get_class($activityItem) == 'Item' && array_search(ucfirst($attributeName), $activityItemsModelClassNamesData) !== false) {
                         try {
                             $modelDerivationPathToItem = RuntimeUtil::getModelDerivationPathToItem(ucfirst($attributeName));
                             $castedDownModel = $activityItem->castDown(array($modelDerivationPathToItem));
                             if (ucfirst($attributeName) == get_class($castedDownModel)) {
                                 $attributeAccessorString = str_replace($attributeName . '->', '', $attributeAccessorString);
                                 return static::resolveMergeTagToStandardOrRelatedAttribute($attributeAccessorString, $castedDownModel, $language, $timeQualifier, $errorOnFirstMissing, $params);
                             }
                         } catch (NotFoundException $e) {
                             //Do nothing
                         }
                     }
                     unset($activityItemsModelClassNamesData[get_class($activityItem)]);
                 }
                 foreach ($activityItemsModelClassNamesData as $relationModelClassName) {
                     if (ucfirst($attributeName) == $relationModelClassName) {
                         $model = new $relationModelClassName();
                         $attributeAccessorString = str_replace($attributeName . '->', '', $attributeAccessorString);
                         return static::resolveMergeTagToStandardOrRelatedAttribute($attributeAccessorString, $model, $language, $timeQualifier, $errorOnFirstMissing, $params);
                     }
                 }
             }
             return static::PROPERTY_NOT_FOUND;
         } elseif ($model->{$attributeName} instanceof CurrencyValue) {
             $model = $model->{$attributeName};
             if ($attributeName === $attributeAccessorString) {
                 $attributeAccessorString = null;
             } else {
                 $attributeAccessorString = str_replace($attributeName . '->', '', $attributeAccessorString);
             }
             if (empty($attributeAccessorString)) {
                 // If a user specific a relation merge tag but not a property, we assume he meant "value" property.
                 $currencyValueModel = $model;
                 $value = static::getAttributeValue($currencyValueModel, 'value', $timeQualifier);
                 return CLocale::getInstance($language)->getCurrencySymbol($currencyValueModel->currency->code) . $value;
                 // We can't use code below because it converts integer values in flat and also add slashes to '.' in float numbers
                 //return Yii::app()->numberFormatter->formatCurrency($value,
                 //    $currencyValueModel->currency->code);
             }
             return static::resolveMergeTagToStandardOrRelatedAttribute($attributeAccessorString, $model, $language, $timeQualifier, $errorOnFirstMissing, $params);
         } elseif ($model->{$attributeName} instanceof CustomField) {
             $value = static::getAttributeValue($model->{$attributeName}, 'value', $timeQualifier);
             // TODO: @Shoaibi/@Jason: Low: need to apply localizations(Date/time/currency formats, ...) here besides translation
             if ($value) {
                 $value = Zurmo::t($model::getModuleClassName(), $value, array(), null, $language);
             }
             return $value;
         } elseif ($model->isRelation($attributeName)) {
             $model = $model->{$attributeName};
             if ($attributeName === $attributeAccessorString) {
                 $attributeAccessorString = null;
             } else {
                 $attributeAccessorString = str_replace($attributeName . '->', '', $attributeAccessorString);
             }
             if (empty($attributeAccessorString)) {
                 // If a user specific a relation merge tag but not a property, we assume he meant "value" property.
                 if (empty($timeQualifier)) {
                     return strval($model);
                 } else {
                     return static::PROPERTY_NOT_FOUND;
                 }
             }
             if ($model instanceof RedBeanModels) {
                 $modelClassName = $model->getModelClassName();
                 if ($attributeAccessorString == lcfirst($modelClassName)) {
                     $values = array();
                     foreach ($model as $relatedModel) {
                         $values[] = strval($relatedModel);
                     }
                     return ArrayUtil::stringify($values);
                 }
             }
             return static::resolveMergeTagToStandardOrRelatedAttribute($attributeAccessorString, $model, $language, $timeQualifier, $errorOnFirstMissing, $params);
         } else {
             $attributeType = ModelAttributeToMixedTypeUtil::getType($model, $attributeName);
             //We don't have any accessor operator after the attributeName e.g. its the last in list
             if ($attributeName === $attributeAccessorString) {
                 $content = static::getAttributeValue($model, $attributeName, $timeQualifier);
                 if ($attributeType == 'DateTime') {
                     $content .= ' GMT';
                 }
                 return $content;
             } else {
                 return static::PROPERTY_NOT_FOUND;
             }
         }
     }
 }
 /**
  * @depends testResolveManageSubscriptionsUrlWithMissingSomeOptionalParams
  */
 public function testResolveManageSubscriptionsUrlWithoutModel()
 {
     $errorOnFirstMissing = MergeTagsToModelAttributesAdapter::DO_NOT_ERROR_ON_FIRST_INVALID_TAG;
     $model = null;
     $params = array('personId' => 1, 'marketingListId' => 2, 'modelId' => 3, 'modelType' => 'AutoresponderItem', 'createNewActivity' => true, 'preview' => false);
     $expected = GlobalMarketingFooterUtil::resolveManageSubscriptionsUrlByArray($params);
     $this->assertContains('marketingLists/external/manageSubscriptions?hash=', $expected);
     $resolved = SpecialMergeTagsAdapter::resolve('manageSubscriptionsUrl', $model, $errorOnFirstMissing, $params);
     $this->assertContains('marketingLists/external/manageSubscriptions?hash=', $resolved);
     $expectedHash = static::extractHashFromUrl($expected);
     $resolvedHash = static::extractHashFromUrl($resolved);
     $expectedReversed = EmailMessageActivityUtil::resolveQueryStringArrayForHash($expectedHash);
     $resolvedReversed = EmailMessageActivityUtil::resolveQueryStringArrayForHash($resolvedHash);
     $this->assertEquals($expectedReversed, $resolvedReversed);
 }
 /**
  * @depends testResolveCurrentYear
  */
 public function testResolveLastYear()
 {
     $resolvedLastYear = SpecialMergeTagsAdapter::resolve('lastYear', null);
     $expectedLastYear = date('Y') - 1;
     $this->assertNotNull($resolvedLastYear);
     $this->assertEquals($expectedLastYear, $resolvedLastYear);
 }