]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - modules/ModuleBuilder/parsers/parser.modifylayoutview.php
Release 6.5.0
[Github/sugarcrm.git] / modules / ModuleBuilder / parsers / parser.modifylayoutview.php
1 <?php
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-2012 SugarCRM Inc.
7  * 
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.
14  * 
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
18  * details.
19  * 
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
23  * 02110-1301 USA.
24  * 
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.
27  * 
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.
31  * 
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  ********************************************************************************/
38
39
40 require_once ('modules/ModuleBuilder/parsers/ModuleBuilderParser.php');
41
42 class ParserModifyLayoutView extends ModuleBuilderParser
43 {
44
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
57
58
59     /**
60      * Constructor
61      */
62     function init ($module, $view, $submittedLayout = false)
63     {
64         $this->_view = ucfirst($view);
65         $this->_module = $module;
66         $this->language_module = $module;
67
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;
72
73         $this->_sourceView = $this->_view;
74         $this->_originalFile = $file ;
75         $this->_sourceFile = $file;
76         if (is_file($this->_workingFile))
77         {
78             $this->_sourceFile = $this->_workingFile;
79             $this->usingWorkingFile = true;
80         }
81         else if (is_file($this->_customFile))
82         {
83             $this->_sourceFile = $this->_customFile;
84         }
85         else if (! is_file($this->_sourceFile))
86         {
87             // if we don't have ANY defined metadata then improvise as best we can
88             if (strtolower($this->_view) == 'quickcreate')
89             {
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))
93                 {
94                     $this->_sourceFile = "custom/" . $this->_sourceFile;
95                 }
96                 $this->_sourceView = 'EditView';
97             }
98             else
99             {
100                 $this->_fatalError('parser.modifylayout.php->init(): no metadata for '.$this->_module.' '.$this->_view);
101             }
102         }
103
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;
109
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'];
113
114         if ($submittedLayout)
115         {
116             // replace the definitions with the new submitted layout
117             $this->_loadLayoutFromRequest();
118         } else
119         {
120             $this->_padFields(); // destined for a View, so we want to add in (empty) fields
121         }
122 //      $GLOBALS['log']->debug($this->_viewdefs['panels']);
123
124     }
125
126     function getAvailableFields ()
127     {
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))
133         {
134             foreach ($this->_viewdefs ['panels'] as $panel)
135             {
136                 foreach ($panel as $row)
137                 {
138                     foreach ($row as $fieldArray)
139                     { // fieldArray is an array('name'=>name,'label'=>label)
140                         if (isset($fieldArray ['name']))
141                         {
142                             unset($availableFields [$fieldArray ['name']]);
143                             $GLOBALS['log']->debug( get_class($this)."->getAvailableFields(): removing ".$fieldArray ['name'] );
144                         }
145                     }
146                 }
147             }
148         }
149         return $availableFields;
150     }
151
152     function getLayout ()
153     {
154         return $this->_viewdefs ['panels'];
155     }
156
157     function writeWorkingFile ()
158     {
159         $this->_writeToFile($this->_workingFile,$this->_view,$this->_module,$this->_viewdefs,$this->_variables);
160     }
161
162     function handleSave ()
163     {
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')
168         {
169                 TemplateHandler::clearCache($this->_module,"form_SubPanelQuickCreate_{$this->_module}.tpl");
170                 TemplateHandler::clearCache($this->_module,"form_DCQuickCreate_{$this->_module}.tpl");
171         } 
172         else 
173         {
174                 TemplateHandler::clearCache($this->_module,"{$this->_view}.tpl");
175         }
176
177     }
178
179     function loadModule ($module, $view)
180     {
181         $this->_viewdefs = array();
182         $viewdefs = null;
183
184         $loaded = $this->_loadFromFile($view,$this->_sourceFile,$module);
185         $this->_viewdefs = $loaded['viewdefs'][$module][$view];
186         $this->_variables = $loaded['variables'];
187     }
188
189     /**
190      * Load the canonical panel layout from the submitted form
191      *
192      */
193     function _loadLayoutFromRequest ()
194     {
195
196         $i = 1;
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)
199         {
200             $panelMap [$i ++] = $panelID;
201         }
202         // replace any old values with new panel labels from the request
203         foreach ($_REQUEST as $key => $value)
204         {
205             $components = explode('-', $key);
206             if ($components [0] == 'panel')
207             {
208                 $panelMap [$components ['1']] = $value;
209             }
210         }
211
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
217
218         if ($this->maxColumns < 1)
219         {
220             $this->_fatalError("EditDetailViewParser:invalid maxColumns=" . $this->maxColumns);
221         }
222
223         foreach ($_REQUEST as $slot => $value)
224         {
225             $slotComponents = explode('-', $slot); // [0] = 'slot', [1] = panel #, [2] = slot #, [3] = property name
226             if ($slotComponents [0] == 'slot')
227             {
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]))
234                 {
235                     $this->_viewdefs ['panels'] [$panelID] [$rowID] [$colID] = $origFieldDefs [$value];
236                 }
237                 else
238                 {
239                     $property = $slotComponents ['3'];
240                     if ($value == '(filler)')
241                     {
242                         $this->_viewdefs ['panels'] [$panelID] [$rowID] [$colID] = NULL;
243                     } else
244                     {
245                         $this->_viewdefs ['panels'] [$panelID] [$rowID] [$colID] [$property] = $value;
246                     }
247                 }
248             }
249         }
250
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)
254         {
255             // remove all (empty)s
256             foreach ($panel as $rowID => $row)
257             {
258                 $startOfRow = true;
259                 $offset = 0;
260                 foreach ($row as $colID => $col)
261                 {
262                     if ($col ['name'] == '(empty)')
263                     {
264                         // if a leading (empty) then remove (by noting that remaining fields need to be shuffled along)
265                         if ($startOfRow)
266                         {
267                             $offset ++;
268                         }
269                         unset($row [$colID]);
270                     } else
271                     {
272                         $startOfRow = false;
273                     }
274                 }
275                 // reindex to remove leading (empty)s
276                 $newRow = array();
277                 foreach ($row as $colID => $col)
278                 {
279                     $newRow [$colID - $offset] = $col;
280                 }
281                 $this->_viewdefs ['panels'] [$panelID] [$rowID] = $newRow;
282             }
283         }
284         //          _pp($this->_viewdefs);
285     }
286
287     function _padFields ()
288     {
289         if (! empty($this->_viewdefs))
290         {
291             foreach ($this->_viewdefs ['panels'] as $panelID => $panel)
292             {
293                 $column = 0;
294                 foreach ($panel as $rowID => $row)
295                 {
296                     // pad between fields on a row
297                     foreach ($row as $colID => $col)
298                     {
299                         for ($i = $column + 1 ; $i < $colID ; $i ++ )
300                         {
301                             $row [$i] = array('name' => '(empty)', 'label' => '(empty)');
302                         }
303                         $column = $colID;
304                     }
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 ++ )
309                         {
310                             $row [$i] = array('name' => '(empty)', 'label' => '(empty)');
311                         }
312                     }
313                     ksort($row);
314                     $this->_viewdefs ['panels'] [$panelID] [$rowID] = $row;
315                 }
316             }
317         }
318     }
319
320
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)
324     {
325
326         // if a panelID was not passed, use the first available panel in the list
327         if (!$panelID) {
328             $panels = array_keys($this->_viewdefs['panels']);
329             $panelID = $panels[0];
330         }
331
332         if (isset($this->_viewdefs ['panels'] [$panelID]))
333         {
334
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;
341                         }
342                         elseif ($col['name'] == '(empty)') {
343                             unset($this->_viewdefs['panels'][$loop_panelID][$row_id][$col_id]);
344                         }
345                     }
346                 }
347             }
348
349             $panel = $this->_viewdefs ['panels'] [$panelID];
350             $lastrow = count($panel) - 1; // index starts at 0
351             $lastcol = count($panel [$lastrow]);
352
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)
356             {
357                 $lastrow ++;
358                 $this->_viewdefs ['panels'] [$panelID] [$lastrow] = array();
359                 $lastcol = 0;
360             }
361
362             $this->_viewdefs ['panels'] [$panelID] [$lastrow] [$lastcol] = $properties;
363         }
364     }
365
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 ()
368     {
369         $modelFields = array();
370         $origViewDefs = $this->_getOrigFieldViewDefs();
371 //        $GLOBALS['log']->debug("Original viewdefs = ".print_r($origViewDefs,true));
372         foreach ($origViewDefs as $field => $def)
373         {
374             if (!empty($field))
375             {
376                 if (! is_array($def)) {
377                     $def = array('name' => $field);
378                 }
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']))
381                         {
382                             if (! empty($this->_fieldDefs [$field] ['vname']))
383                             {
384                                 $def ['label'] = $this->_fieldDefs [$field] ['vname'];
385                             } else
386                             {
387                                 $def ['label'] = $field;
388                             }
389                         }
390                 $modelFields[$field] = array('name' => $field, 'label' => $def ['label']);
391             }
392         }
393         $GLOBALS['log']->debug(print_r($modelFields,true));
394         foreach ($this->_fieldDefs as $field => $def)
395         {
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')))
398             {
399                 $label = isset($def['vname']) ? $def['vname'] : $def['name'];
400                 $modelFields [$field] = array('name' => $field, 'label' => $label);
401             }
402             else
403             {
404                 $GLOBALS['log']->debug( get_class($this)."->_getModelFields(): skipping $field from modelFields as it fails the test for inclusion");
405             }
406         }
407         $GLOBALS['log']->debug( get_class($this)."->_getModelFields(): remaining entries in modelFields are: ".implode(",",array_keys($modelFields)));
408         return $modelFields;
409     }
410
411     function _parseData ($panels)
412     {
413         $fields = array();
414         if (empty($panels))
415         return;
416
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)
420         {
421             $keys = array_keys($panels);
422             if (is_numeric($keys [0]))
423             {
424                 $defaultPanel = $panels;
425                 unset($panels); //blow away current value
426                 $panels [''] = $defaultPanel;
427             }
428         }
429
430         foreach ($panels as $panelID => $panel)
431         {
432             foreach ($panel as $rowID => $row)
433             {
434                 foreach ($row as $colID => $col)
435                 {
436                     $properties = array();
437
438                     if (! empty($col))
439                     {
440                         if (is_string($col))
441                         {
442                             $properties ['name'] = $col;
443                         } else if (! empty($col ['name']))
444                         {
445                             $properties = $col;
446                         }
447                     } else
448                     {
449                         $properties ['name'] = translate('LBL_FILLER');
450                     }
451
452                     if (! empty($properties ['name']))
453                     {
454
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']))
457                         {
458                             if (! empty($this->_fieldDefs [$properties ['name']] ['vname']))
459                             {
460                                 $properties ['label'] = $this->_fieldDefs [$properties ['name']] ['vname'];
461                             } else
462                             {
463                                 $properties ['label'] = $properties ['name'];
464                             }
465                         }
466
467                         $displayData[strtoupper($panelID)] [$rowID] [$colID] = $properties;
468
469                     }
470                 }
471             }
472         }
473         return $displayData;
474     }
475
476     function _getOrigFieldViewDefs ()
477     {
478         $origFieldDefs = array();
479         $GLOBALS['log']->debug("Original File = ".$this->_originalFile);
480         if (file_exists($this->_originalFile))
481         {
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)
488             {
489                 $keys = array_keys($origdefs);
490                 if (is_numeric($keys [0]))
491                 {
492                     $defaultPanel = $origdefs;
493                     unset($origdefs); //blow away current value
494                     $origdefs [''] = $defaultPanel;
495                 }
496             }
497             foreach ($origdefs as $pname => $paneldef)
498             {
499                 foreach ($paneldef as $row)
500                 {
501                     foreach ($row as $fieldDef)
502                     {
503                         if (is_array($fieldDef))
504                         {
505                             $fieldName = $fieldDef ['name'];
506                         }
507                         else
508                         {
509                             $fieldName = $fieldDef;
510                         }
511                         $origFieldDefs [$fieldName] = $fieldDef;
512                     }
513                 }
514             }
515         }
516
517         return $origFieldDefs;
518     }
519
520 }
521 ?>