Account Suspended
Account Suspended
This Account has been suspended.
Contact your hosting provider for more information.
 All Data Structures Functions Variables Pages
DynamicSearchUtil.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  {
42  public static function getSearchableAttributesAndLabels($viewClassName, $modelClassName)
43  {
44  assert('is_string($viewClassName)');
45  assert('is_string($modelClassName) && is_subclass_of($modelClassName, "RedBeanModel")');
46  $searchFormClassName = $viewClassName::getModelForMetadataClassName();
47  $editableMetadata = $viewClassName::getMetadata();
48  $designerRulesType = $viewClassName::getDesignerRulesType();
49  $designerRulesClassName = $designerRulesType . 'DesignerRules';
50  $designerRules = new $designerRulesClassName();
51  $modelAttributesAdapter = DesignerModelToViewUtil::getModelAttributesAdapter($viewClassName, $modelClassName);
52  $derivedAttributesAdapter = new DerivedAttributesAdapter($modelClassName);
53  $attributeCollection = array_merge($modelAttributesAdapter->getAttributes(),
54  $derivedAttributesAdapter->getAttributes());
56  $attributeCollection,
57  $designerRules,
58  $editableMetadata
59  );
60  $attributeIndexOrDerivedTypeAndLabels = array();
61  foreach ($attributesLayoutAdapter->makeDesignerLayoutAttributes()->get() as $attributeIndexOrDerivedType => $data)
62  {
63  //special case with anyMixedAttributes since it is searchable but in the basic search part so never dynamically searchable
64  if ($searchFormClassName::isAttributeSearchable($attributeIndexOrDerivedType) && $attributeIndexOrDerivedType != 'anyMixedAttributes')
65  {
66  $attributeIndexOrDerivedTypeAndLabels[$attributeIndexOrDerivedType] = $data['attributeLabel'];
67  }
68  }
69  self::resolveAndAddViewDefinedNestedAttributes($modelAttributesAdapter->getModel(), $viewClassName, $attributeIndexOrDerivedTypeAndLabels);
70 
71  if (is_subclass_of($viewClassName, 'DynamicSearchView'))
72  {
73  $viewClassName::
74  resolveAttributeIndexOrDerivedTypeAndLabelsForDynamicSearchRow($attributeIndexOrDerivedTypeAndLabels);
75  }
76  asort($attributeIndexOrDerivedTypeAndLabels);
77  return $attributeIndexOrDerivedTypeAndLabels;
78  }
79 
80  public static function getCellElement($viewClassName, $modelClassName, $elementName)
81  {
82  assert('is_string($viewClassName)');
83  assert('is_string($modelClassName) && is_subclass_of($modelClassName, "RedBeanModel")');
84  assert('is_string($elementName)');
85  $editableMetadata = $viewClassName::getMetadata();
86  $designerRulesType = $viewClassName::getDesignerRulesType();
87  $designerRulesClassName = $designerRulesType . 'DesignerRules';
88  $designerRules = new $designerRulesClassName();
89  $modelAttributesAdapter = DesignerModelToViewUtil::getModelAttributesAdapter($viewClassName, $modelClassName);
90  $derivedAttributesAdapter = new DerivedAttributesAdapter($modelClassName);
91  $attributeCollection = array_merge($modelAttributesAdapter->getAttributes(),
92  $derivedAttributesAdapter->getAttributes());
94  $attributeCollection,
95  $designerRules,
96  $editableMetadata
97  );
98 
99  $derivedAttributes = $attributesLayoutAdapter->getAvailableDerivedAttributeTypes();
100  $placeableLayoutAttributes = $attributesLayoutAdapter->getPlaceableLayoutAttributes();
101  if (in_array($elementName, $derivedAttributes))
102  {
103  $element = array('attributeName' => 'null', 'type' => $elementName); // Not Coding Standard
104  }
105  elseif (isset($placeableLayoutAttributes[$elementName]) &&
106  $placeableLayoutAttributes[$elementName]['elementType'] == 'DropDownDependency')
107  {
108  throw new NotSupportedException();
109  }
110  elseif (isset($placeableLayoutAttributes[$elementName]))
111  {
112  $element = array(
113  'attributeName' => $elementName,
114  'type' => $placeableLayoutAttributes[$elementName]['elementType']
115  );
116  }
117  else
118  {
119  throw new NotSupportedException();
120  }
121  return $designerRules->formatSavableElement($element, $viewClassName);
122  }
123 
124  public static function resolveAndAddViewDefinedNestedAttributes($model, $viewClassName, & $attributeIndexOrDerivedTypeAndLabels)
125  {
126  assert('$model instanceof SearchForm || $model instanceof RedBeanModel');
127  assert('is_string($viewClassName)');
128  assert('is_array($attributeIndexOrDerivedTypeAndLabels)');
129  $metadata = $viewClassName::getMetadata();
130  if (isset($metadata['global']['definedNestedAttributes']))
131  {
132  foreach ($metadata['global']['definedNestedAttributes'] as $definedNestedAttribute)
133  {
134  $attributeIndexOrDerivedLabel = null;
135  $attributeIndexOrDerivedType = self::makeDefinedNestedAttributeIndexOrDerivedTypeRecursively(
136  $model,
137  $attributeIndexOrDerivedLabel,
138  $definedNestedAttribute);
139  if ($attributeIndexOrDerivedLabel == null)
140  {
141  throw new NotSupportedException();
142  }
143  $attributeIndexOrDerivedTypeAndLabels[$attributeIndexOrDerivedType] = $attributeIndexOrDerivedLabel;
144  }
145  }
146  }
147 
148  protected static function makeDefinedNestedAttributeIndexOrDerivedTypeRecursively($model, & $attributeIndexOrDerivedLabel, $definedNestedAttribute)
149  {
150  assert('$model instanceof SearchForm || $model instanceof RedBeanModel');
151  assert('is_string($attributeIndexOrDerivedLabel) || $attributeIndexOrDerivedLabel == null');
152  assert('is_array($definedNestedAttribute)');
153  if (count($definedNestedAttribute) > 1)
154  {
155  //Each defined attribute should be in its own sub-array.
156  throw new NotSupportedException();
157  }
158  foreach ($definedNestedAttribute as $positionOrAttributeName => $nestedAttributeDataOrAttributeName)
159  {
160  if (is_array($nestedAttributeDataOrAttributeName))
161  {
162  $attributeIndexOrDerivedLabel .= $model->getAttributeLabel($positionOrAttributeName) . ' - ';
164  $model,
165  $positionOrAttributeName);
166  $string = self::makeDefinedNestedAttributeIndexOrDerivedTypeRecursively(
167  $modelToUse,
168  $attributeIndexOrDerivedLabel,
169  $nestedAttributeDataOrAttributeName);
170  return $positionOrAttributeName . FormModelUtil::RELATION_DELIMITER . $string;
171  }
172  else
173  {
174  $attributeIndexOrDerivedLabel .= $model->getAttributeLabel($nestedAttributeDataOrAttributeName);
175  return $nestedAttributeDataOrAttributeName;
176  }
177  }
178  }
179 
180  public static function renderDynamicSearchAttributeInput($viewClassName,
181  $modelClassName,
182  $formModelClassName,
183  $rowNumber,
184  $attributeIndexOrDerivedType,
185  $searchAttributes = array(),
186  $suffix = null)
187  {
188  assert('is_string($viewClassName)');
189  assert('is_string($modelClassName)');
190  assert('is_string($formModelClassName)');
191  assert('is_int($rowNumber)');
192  assert('is_string($attributeIndexOrDerivedType) || $attributeIndexOrDerivedType == null');
193  assert('is_array($searchAttributes)');
194  assert('is_string($suffix) || $suffix == null');
195  $content = null;
196  if (count(explode(FormModelUtil::RELATION_DELIMITER, $attributeIndexOrDerivedType)) > 1)
197  {
198  $model = new $modelClassName(false);
199  $nestedAttributes = explode(FormModelUtil::RELATION_DELIMITER, $attributeIndexOrDerivedType);
200  $inputPrefix = array($formModelClassName, DynamicSearchForm::DYNAMIC_NAME, $rowNumber);
201  $totalNestedCount = count($nestedAttributes);
202  $processCount = 1;
203  $nestedSearchAttributes = $searchAttributes;
204 
205  foreach ($nestedAttributes as $attribute)
206  {
207  if ($processCount < $totalNestedCount && isset($nestedSearchAttributes[$attribute]))
208  {
209  $nestedSearchAttributes = $nestedSearchAttributes[$attribute];
210  if (isset($nestedSearchAttributes['relatedData']))
211  {
212  unset($nestedSearchAttributes['relatedData']);
213  }
214  }
215  if ($processCount < $totalNestedCount)
216  {
217  $model = SearchUtil::resolveModelToUseByModelAndAttributeName($model, $attribute);
218  $inputPrefix[] = $attribute;
219  $relatedDataName = Element::resolveInputNamePrefixIntoString($inputPrefix) . '[relatedData]';
220  $content .= ZurmoHtml::hiddenField($relatedDataName, true);
221  }
222  $processCount++;
223  }
224  $attributeIndexOrDerivedType = $attribute;
225  $modelToUse = $model;
226  $modelToUse->setAttributes($nestedSearchAttributes);
227  $cellElementModelClassName = get_class($model->getModel());
228  //Dynamic Search needs to always assume there is an available SearchForm
229  //Always assumes the SearchView to use matches the exact pluralCamelCasedName.
230  //Does not support nested relations to leads persay. It will resolve as a Contact.
231  //This is not a problem since you can't relate a model to a lead, it is related to a contact.
232  //So this scenario would not come up naturally.
233  $moduleClassName = $model->getModel()->getModuleClassName();
234  $viewClassName = $moduleClassName::getPluralCamelCasedName() . 'SearchView';
235  $element = DynamicSearchUtil::getCellElement($viewClassName, $cellElementModelClassName,
236  $attributeIndexOrDerivedType);
237  }
238  else
239  {
240  $model = new $modelClassName(false);
241  $model->setScenario('importModel'); //this is so attributes such as modified user can be set
242  $modelToUse = new $formModelClassName($model);
243  $modelToUse->setAttributes($searchAttributes);
244  $inputPrefix = array($formModelClassName, DynamicSearchForm::DYNAMIC_NAME, $rowNumber);
245  $element = DynamicSearchUtil::getCellElement($viewClassName, $modelClassName,
246  $attributeIndexOrDerivedType);
247  }
248  $form = new NoRequiredsActiveForm();
249  $form->id = "search-form";
250  $element['inputPrefix'] = $inputPrefix;
251  $elementclassname = $element['type'] . 'Element';
252  $element = new $elementclassname($modelToUse, $element['attributeName'],
253  $form, array_slice($element, 2));
254  $element->editableTemplate = '{content}{error}';
255  $content .= $element->render();
256  DropDownUtil::registerScripts(CClientScript::POS_END);
257  return $content;
258  }
259 
260  public static function renderDynamicSearchRowContent($viewClassName,
261  $modelClassName,
262  $formModelClassName,
263  $rowNumber,
264  $attributeIndexOrDerivedType,
265  $inputContent,
266  $suffix = null,
267  $renderAsAjax = false)
268  {
269  assert('is_string($viewClassName)');
270  assert('is_string($modelClassName)');
271  assert('is_string($formModelClassName)');
272  assert('is_int($rowNumber)');
273  assert('is_string($attributeIndexOrDerivedType) || $attributeIndexOrDerivedType == null');
274  assert('is_string($suffix) || $suffix == null');
275  assert('is_bool($renderAsAjax)');
276  $searchableAttributeIndicesAndDerivedTypes = DynamicSearchUtil::
277  getSearchableAttributesAndLabels($viewClassName,
278  $modelClassName);
279  $ajaxOnChangeUrl = Yii::app()->createUrl("zurmo/default/dynamicSearchAttributeInput",
280  array('viewClassName' => $viewClassName,
281  'modelClassName' => $modelClassName,
282  'formModelClassName' => $formModelClassName,
283  'rowNumber' => $rowNumber,
284  'suffix' => $suffix));
285  $rowView = new DynamicSearchRowView(
286  $searchableAttributeIndicesAndDerivedTypes,
287  (int)$rowNumber,
288  $suffix,
289  $formModelClassName,
290  $ajaxOnChangeUrl,
291  $attributeIndexOrDerivedType,
292  $inputContent);
293 
294  if (!$renderAsAjax)
295  {
296  $view = $rowView;
297  }
298  else
299  {
300  $view = new AjaxPageView($rowView);
301  }
302  return ZurmoHtml::tag('div', array('class' => 'dynamic-search-row'), $view->render());
303  }
304 
314  public static function resolveDynamicSearchClausesForModelIdsNeedingToBeItemIds($modelClassName, & $dynamicClauses)
315  {
316  assert(is_array($dynamicClauses) && !empty($dynamicClauses)); // Not Coding Standard
317  $processRecursively = false;
318 
319  foreach ($dynamicClauses as $key => $clause)
320  {
321  // To-do: Should we check only on attributeIndexOrDerivedType key?
322  if (isset($clause['attributeIndexOrDerivedType']))
323  {
324  $attributeIndexOrDerivedType = $clause['attributeIndexOrDerivedType'];
325 
326  if (isset($clause[$attributeIndexOrDerivedType]) && is_array($clause[$attributeIndexOrDerivedType]))
327  {
328  $relation = $clause[$attributeIndexOrDerivedType];
329 
330  if (isset($relation['modelClassName']) &&
331  $relation['modelClassName'] != $modelClassName::getRelationModelClassName($attributeIndexOrDerivedType) &&
332  $modelClassName::getRelationType($attributeIndexOrDerivedType) == RedBeanModel::MANY_MANY &&
333  $modelClassName::getRelationModelClassName($attributeIndexOrDerivedType) == 'Item')
334  {
335  $relClassName = $relation['modelClassName'];
336  $relModel = $relClassName::getById((int)$relation['modelId']);
337  $itemId = $relModel->getClassId('Item');
338  unset($dynamicClauses[$key][$attributeIndexOrDerivedType]['modelClassName']);
339  unset($dynamicClauses[$key][$attributeIndexOrDerivedType]['modelId']);
340  $dynamicClauses[$key][$attributeIndexOrDerivedType]['id'] = $itemId;
341  }
342  }
343  }
344  }
345  }
346  }
347 ?>
static makeAttributesLayoutAdapter($attributeCollection, DesignerRules $designerRules, $editableMetadata)
const RELATION_DELIMITER
static resolveDynamicSearchClausesForModelIdsNeedingToBeItemIds($modelClassName, &$dynamicClauses)
const MANY_MANY
Definition: BeanModel.php:78
static resolveModelToUseByModelAndAttributeName($model, $attributeName)
Definition: SearchUtil.php:481
static getModelAttributesAdapter($viewClassName, $modelClassName)
Generated on Tue Jul 7 2020 07:10:29
Account Suspended
Account Suspended
This Account has been suspended.
Contact your hosting provider for more information.