2 if (! defined('sugarEntry') || ! sugarEntry)
3 die('Not A Valid Entry Point');
4 /*********************************************************************************
5 * SugarCRM Community Edition is a customer relationship management program developed by
6 * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
8 * This program is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU Affero General Public License version 3 as published by the
10 * Free Software Foundation with the addition of the following permission added
11 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
12 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
13 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
15 * This program is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
20 * You should have received a copy of the GNU Affero General Public License along with
21 * this program; if not, see http://www.gnu.org/licenses or write to the Free
22 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
26 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
28 * The interactive user interfaces in modified source and object code versions
29 * of this program must display Appropriate Legal Notices, as required under
30 * Section 5 of the GNU Affero General Public License version 3.
32 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
33 * these Appropriate Legal Notices must retain the display of the "Powered by
34 * SugarCRM" logo. If the display of the logo is not reasonably feasible for
35 * technical reasons, the Appropriate Legal Notices must display the words
36 * "Powered by SugarCRM".
37 ********************************************************************************/
40 require_once ('modules/ModuleBuilder/parsers/ModuleBuilderParser.php');
42 class ParserModifyLayoutView extends ModuleBuilderParser
45 var $maxColumns; // number of columns in this layout
46 var $usingWorkingFile = false; // if a working file exists (used by view.edit.php among others to determine the title for the layout edit panel)
47 var $language_module; // set to module name for studio, passed to the smarty template and used by sugar_translate
48 var $_sourceFile; // private - the source of the layout defn
49 var $_customFile; // private
50 var $_workingFile; // private
51 var $_originalFile; //private
52 var $_moduleVariable; // private - if set, contains the name of the variable containing the module name in the $viewdef file
53 var $_module; // private
54 var $_view; // private
55 var $_viewdefs; // private
56 var $_fieldDefs; // private
62 function init ($module, $view, $submittedLayout = false)
64 $this->_view = ucfirst($view);
65 $this->_module = $module;
66 $this->language_module = $module;
68 $this->_baseDirectory = "modules/{$module}/metadata/";
69 $file = $this->_baseDirectory . strtolower($view) . "defs.php";
70 $this->_customFile = "custom/" . $file;
71 $this->_workingFile = "custom/working/" . $file;
73 $this->_sourceView = $this->_view;
74 $this->_originalFile = $file ;
75 $this->_sourceFile = $file;
76 if (is_file($this->_workingFile))
78 $this->_sourceFile = $this->_workingFile;
79 $this->usingWorkingFile = true;
81 else if (is_file($this->_customFile))
83 $this->_sourceFile = $this->_customFile;
85 else if (! is_file($this->_sourceFile))
87 // if we don't have ANY defined metadata then improvise as best we can
88 if (strtolower($this->_view) == 'quickcreate')
90 // special handling for quickcreates - base the quickcreate on the editview if no quickcreatedef exists
91 $this->_sourceFile = $this->_baseDirectory."editviewdefs.php";
92 if (is_file("custom/" . $this->_sourceFile))
94 $this->_sourceFile = "custom/" . $this->_sourceFile;
96 $this->_sourceView = 'EditView';
100 $this->_fatalError('parser.modifylayout.php->init(): no metadata for '.$this->_module.' '.$this->_view);
104 // get the fieldDefs from the bean
105 $class = $GLOBALS ['beanList'] [$module];
106 require_once ($GLOBALS ['beanFiles'] [$class]);
107 $bean = new $class();
108 $this->_fieldDefs = & $bean->field_defs;
110 $this->loadModule($this->_module, $this->_sourceView);
111 $this->_viewdefs ['panels'] = $this->_parseData($this->_viewdefs['panels']); // put into a canonical format
112 $this->maxColumns = $this->_viewdefs ['templateMeta'] ['maxColumns'];
114 if ($submittedLayout)
116 // replace the definitions with the new submitted layout
117 $this->_loadLayoutFromRequest();
120 $this->_padFields(); // destined for a View, so we want to add in (empty) fields
122 // $GLOBALS['log']->debug($this->_viewdefs['panels']);
126 function getAvailableFields ()
128 // Available fields are those that are in the Model and the original layout definition, but not already shown in the View
129 // So, because the formats of the two are different we brute force loop through View and unset the fields we find in a copy of Model
130 $availableFields = $this->_getModelFields();
131 $GLOBALS['log']->debug( get_class($this)."->getAvailableFields(): _getModelFields returns: ".implode(",",array_keys($availableFields)));
132 if (! empty($this->_viewdefs))
134 foreach ($this->_viewdefs ['panels'] as $panel)
136 foreach ($panel as $row)
138 foreach ($row as $fieldArray)
139 { // fieldArray is an array('name'=>name,'label'=>label)
140 if (isset($fieldArray ['name']))
142 unset($availableFields [$fieldArray ['name']]);
143 $GLOBALS['log']->debug( get_class($this)."->getAvailableFields(): removing ".$fieldArray ['name'] );
149 return $availableFields;
152 function getLayout ()
154 return $this->_viewdefs ['panels'];
157 function writeWorkingFile ()
159 $this->_writeToFile($this->_workingFile,$this->_view,$this->_module,$this->_viewdefs,$this->_variables);
162 function handleSave ()
164 $this->_writeToFile($this->_customFile,$this->_view,$this->_module,$this->_viewdefs,$this->_variables);
165 // now clear the cache so that the results are immediately visible
166 include_once('include/TemplateHandler/TemplateHandler.php');
167 if (strtolower($this->_view) == 'quickcreate')
169 TemplateHandler::clearCache($this->_module,"form_SubPanelQuickCreate_{$this->_module}.tpl");
170 TemplateHandler::clearCache($this->_module,"form_DCQuickCreate_{$this->_module}.tpl");
174 TemplateHandler::clearCache($this->_module,"{$this->_view}.tpl");
179 function loadModule ($module, $view)
181 $this->_viewdefs = array();
184 $loaded = $this->_loadFromFile($view,$this->_sourceFile,$module);
185 $this->_viewdefs = $loaded['viewdefs'][$module][$view];
186 $this->_variables = $loaded['variables'];
190 * Load the canonical panel layout from the submitted form
193 function _loadLayoutFromRequest ()
197 // set up the map of panel# (as provided in the _REQUEST) to panel ID (as used in $this->_viewdefs['panels'])
198 foreach ($this->_viewdefs ['panels'] as $panelID => $panel)
200 $panelMap [$i ++] = $panelID;
202 // replace any old values with new panel labels from the request
203 foreach ($_REQUEST as $key => $value)
205 $components = explode('-', $key);
206 if ($components [0] == 'panel')
208 $panelMap [$components ['1']] = $value;
212 $olddefs = $this->_viewdefs ['panels'];
213 $origFieldDefs = $this->_getOrigFieldViewDefs();
214 // $GLOBALS['log']->debug('origFieldDefs');
215 // $GLOBALS['log']->debug($origFieldDefs);
216 $this->_viewdefs ['panels'] = null; // because the new field properties should replace the old fields, not be merged
218 if ($this->maxColumns < 1)
220 $this->_fatalError("EditDetailViewParser:invalid maxColumns=" . $this->maxColumns);
223 foreach ($_REQUEST as $slot => $value)
225 $slotComponents = explode('-', $slot); // [0] = 'slot', [1] = panel #, [2] = slot #, [3] = property name
226 if ($slotComponents [0] == 'slot')
228 $slotNumber = $slotComponents ['2'];
229 $panelID = $panelMap [$slotComponents ['1']];
230 $rowID = floor($slotNumber / $this->maxColumns);
231 $colID = $slotNumber - ($rowID * $this->maxColumns);
232 //If the original editview defined this field, copy that over.
233 if ($slotComponents ['3'] == 'name' && isset($origFieldDefs [$value]) && is_array($origFieldDefs [$value]))
235 $this->_viewdefs ['panels'] [$panelID] [$rowID] [$colID] = $origFieldDefs [$value];
239 $property = $slotComponents ['3'];
240 if ($value == '(filler)')
242 $this->_viewdefs ['panels'] [$panelID] [$rowID] [$colID] = NULL;
245 $this->_viewdefs ['panels'] [$panelID] [$rowID] [$colID] [$property] = $value;
251 // Now handle the (empty) fields - first non-(empty) field goes in at column 0; all other (empty)'s removed
252 // Do this AFTER reading in all the $_REQUEST parameters as can't guarantee the order of those, and we need to operate on complete rows
253 foreach ($this->_viewdefs ['panels'] as $panelID => $panel)
255 // remove all (empty)s
256 foreach ($panel as $rowID => $row)
260 foreach ($row as $colID => $col)
262 if ($col ['name'] == '(empty)')
264 // if a leading (empty) then remove (by noting that remaining fields need to be shuffled along)
269 unset($row [$colID]);
275 // reindex to remove leading (empty)s
277 foreach ($row as $colID => $col)
279 $newRow [$colID - $offset] = $col;
281 $this->_viewdefs ['panels'] [$panelID] [$rowID] = $newRow;
284 // _pp($this->_viewdefs);
287 function _padFields ()
289 if (! empty($this->_viewdefs))
291 foreach ($this->_viewdefs ['panels'] as $panelID => $panel)
294 foreach ($panel as $rowID => $row)
296 // pad between fields on a row
297 foreach ($row as $colID => $col)
299 for ($i = $column + 1 ; $i < $colID ; $i ++ )
301 $row [$i] = array('name' => '(empty)', 'label' => '(empty)');
305 // now pad out to the end of the row
306 if (($column + 1) < $this->maxColumns)
307 { // last column is maxColumns-1
308 for ($i = $column + 1 ; $i < $this->maxColumns ; $i ++ )
310 $row [$i] = array('name' => '(empty)', 'label' => '(empty)');
314 $this->_viewdefs ['panels'] [$panelID] [$rowID] = $row;
321 // add a new field to the end of a panel
322 // don't write out (caller should call handleSave() when ready)
323 function _addField ($properties, $panelID = FALSE)
326 // if a panelID was not passed, use the first available panel in the list
328 $panels = array_keys($this->_viewdefs['panels']);
329 $panelID = $panels[0];
332 if (isset($this->_viewdefs ['panels'] [$panelID]))
335 // need to clean up the viewdefs before writing them -- Smarty will fail if any fillers/empties are present
336 foreach ($this->_viewdefs['panels'] as $loop_panelID => $panel_contents) {
337 foreach ($panel_contents as $row_id => $row) {
338 foreach ($row as $col_id => $col) {
339 if ($col['name'] == '(filler)') {
340 $this->_viewdefs['panels'][$loop_panelID][$row_id][$col_id] = NULL;
342 elseif ($col['name'] == '(empty)') {
343 unset($this->_viewdefs['panels'][$loop_panelID][$row_id][$col_id]);
349 $panel = $this->_viewdefs ['panels'] [$panelID];
350 $lastrow = count($panel) - 1; // index starts at 0
351 $lastcol = count($panel [$lastrow]);
353 // if we're on the last column of the last row, start a new row
354 // print "lastrow=$lastrow lastcol=$lastcol";
355 if ($lastcol >= $this->maxColumns)
358 $this->_viewdefs ['panels'] [$panelID] [$lastrow] = array();
362 $this->_viewdefs ['panels'] [$panelID] [$lastrow] [$lastcol] = $properties;
366 /* getModelFields returns an array of all fields stored in the database for this module plus those fields in the original layout definition (so we get fields such as Team ID)*/
367 function _getModelFields ()
369 $modelFields = array();
370 $origViewDefs = $this->_getOrigFieldViewDefs();
371 // $GLOBALS['log']->debug("Original viewdefs = ".print_r($origViewDefs,true));
372 foreach ($origViewDefs as $field => $def)
376 if (! is_array($def)) {
377 $def = array('name' => $field);
379 // get this field's label - if it has not been explicitly provided, see if the fieldDefs has a label for this field, and if not fallback to the field name
380 if (! isset($def ['label']))
382 if (! empty($this->_fieldDefs [$field] ['vname']))
384 $def ['label'] = $this->_fieldDefs [$field] ['vname'];
387 $def ['label'] = $field;
390 $modelFields[$field] = array('name' => $field, 'label' => $def ['label']);
393 $GLOBALS['log']->debug(print_r($modelFields,true));
394 foreach ($this->_fieldDefs as $field => $def)
396 if ((!empty($def['studio']) && $def['studio'] == 'visible')
397 || (empty($def['studio']) && (empty($def ['source']) || $def ['source'] == 'db' || $def ['source'] == 'custom_fields') && $def ['type'] != 'id' && strcmp($field, 'deleted') != 0 && (empty($def ['dbType']) || $def ['dbType'] != 'id') && (empty($def ['dbtype']) || $def ['dbtype'] != 'id')))
399 $label = isset($def['vname']) ? $def['vname'] : $def['name'];
400 $modelFields [$field] = array('name' => $field, 'label' => $label);
404 $GLOBALS['log']->debug( get_class($this)."->_getModelFields(): skipping $field from modelFields as it fails the test for inclusion");
407 $GLOBALS['log']->debug( get_class($this)."->_getModelFields(): remaining entries in modelFields are: ".implode(",",array_keys($modelFields)));
411 function _parseData ($panels)
417 // Fix for a flexibility in the format of the panel sections - if only one panel, then we don't have a panel level defined, it goes straight into rows
418 // See EditView2 for similar treatment
419 if (! empty($panels) && count($panels) > 0)
421 $keys = array_keys($panels);
422 if (is_numeric($keys [0]))
424 $defaultPanel = $panels;
425 unset($panels); //blow away current value
426 $panels [''] = $defaultPanel;
430 foreach ($panels as $panelID => $panel)
432 foreach ($panel as $rowID => $row)
434 foreach ($row as $colID => $col)
436 $properties = array();
442 $properties ['name'] = $col;
443 } else if (! empty($col ['name']))
449 $properties ['name'] = translate('LBL_FILLER');
452 if (! empty($properties ['name']))
455 // get this field's label - if it has not been explicity provided, see if the fieldDefs has a label for this field, and if not fallback to the field name
456 if (! isset($properties ['label']))
458 if (! empty($this->_fieldDefs [$properties ['name']] ['vname']))
460 $properties ['label'] = $this->_fieldDefs [$properties ['name']] ['vname'];
463 $properties ['label'] = $properties ['name'];
467 $displayData[strtoupper($panelID)] [$rowID] [$colID] = $properties;
476 function _getOrigFieldViewDefs ()
478 $origFieldDefs = array();
479 $GLOBALS['log']->debug("Original File = ".$this->_originalFile);
480 if (file_exists($this->_originalFile))
482 include ($this->_originalFile);
483 $origdefs = $viewdefs [$this->_module] [$this->_sourceView] ['panels'];
484 // $GLOBALS['log']->debug($origdefs);
485 // Fix for a flexibility in the format of the panel sections - if only one panel, then we don't have a panel level defined, it goes straight into rows
486 // See EditView2 for similar treatment
487 if (! empty($origdefs) && count($origdefs) > 0)
489 $keys = array_keys($origdefs);
490 if (is_numeric($keys [0]))
492 $defaultPanel = $origdefs;
493 unset($origdefs); //blow away current value
494 $origdefs [''] = $defaultPanel;
497 foreach ($origdefs as $pname => $paneldef)
499 foreach ($paneldef as $row)
501 foreach ($row as $fieldDef)
503 if (is_array($fieldDef))
505 $fieldName = $fieldDef ['name'];
509 $fieldName = $fieldDef;
511 $origFieldDefs [$fieldName] = $fieldDef;
517 return $origFieldDefs;