]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - modules/ModuleBuilder/parsers/views/GridLayoutMetaDataParser.php
Release 6.5.6
[Github/sugarcrm.git] / modules / ModuleBuilder / parsers / views / GridLayoutMetaDataParser.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/views/AbstractMetaDataParser.php' ;
41 require_once 'modules/ModuleBuilder/parsers/views/MetaDataParserInterface.php' ;
42 require_once 'modules/ModuleBuilder/parsers/constants.php' ;
43
44 class GridLayoutMetaDataParser extends AbstractMetaDataParser implements MetaDataParserInterface
45 {
46
47     static $variableMap = array (
48         MB_EDITVIEW => 'EditView' ,
49         MB_DETAILVIEW => 'DetailView' ,
50         MB_QUICKCREATE => 'QuickCreate',
51         ) ;
52
53         protected $FILLER ;
54
55     /*
56      * Constructor
57      * @param string view           The view type, that is, editview, searchview etc
58      * @param string moduleName     The name of the module to which this view belongs
59      * @param string packageName    If not empty, the name of the package to which this view belongs
60      */
61     function __construct ($view , $moduleName , $packageName = '')
62     {
63         $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->__construct( {$view} , {$moduleName} , {$packageName} )" ) ;
64
65         $view = strtolower ( $view ) ;
66
67                 $this->FILLER = array ( 'name' => MBConstants::$FILLER['name'] , 'label' => translate ( MBConstants::$FILLER['label'] ) ) ;
68
69         $this->_moduleName = $moduleName ;
70         $this->_view = $view ;
71
72         if (empty ( $packageName ))
73         {
74             require_once 'modules/ModuleBuilder/parsers/views/DeployedMetaDataImplementation.php' ;
75             $this->implementation = new DeployedMetaDataImplementation ( $view, $moduleName, self::$variableMap ) ;
76         } else
77         {
78             require_once 'modules/ModuleBuilder/parsers/views/UndeployedMetaDataImplementation.php' ;
79             $this->implementation = new UndeployedMetaDataImplementation ( $view, $moduleName, $packageName ) ;
80         }
81
82         $viewdefs = $this->implementation->getViewdefs () ;
83         if (!isset(self::$variableMap [ $view ]))
84             self::$variableMap [ $view ] = $view;
85
86         if (!isset($viewdefs [ self::$variableMap [ $view ]])){
87             sugar_die ( get_class ( $this ) . ": incorrect view variable for $view" ) ;
88         }
89
90         $viewdefs = $viewdefs [ self::$variableMap [ $view ] ] ;
91         if (! isset ( $viewdefs [ 'templateMeta' ] ))
92             sugar_die ( get_class ( $this ) . ": missing templateMeta section in layout definition (case sensitive)" ) ;
93
94         if (! isset ( $viewdefs [ 'panels' ] ))
95             sugar_die ( get_class ( $this ) . ": missing panels section in layout definition (case sensitive)" ) ;
96
97         $this->_viewdefs = $viewdefs ;
98         if ($this->getMaxColumns () < 1)
99             sugar_die ( get_class ( $this ) . ": maxColumns=" . $this->getMaxColumns () . " - must be greater than 0!" ) ;
100
101         $this->_fielddefs =  $this->implementation->getFielddefs() ;
102         $this->_standardizeFieldLabels( $this->_fielddefs );
103         $this->_viewdefs [ 'panels' ] = $this->_convertFromCanonicalForm ( $this->_viewdefs [ 'panels' ] , $this->_fielddefs ) ; // put into our internal format
104         $this->_originalViewDef = $this->getFieldsFromLayout($this->implementation->getOriginalViewdefs ());
105     }
106
107     /*
108      * Save a draft layout
109      */
110     function writeWorkingFile ($populate = true)
111     {
112         if ($populate)
113             $this->_populateFromRequest ( $this->_fielddefs ) ;
114         
115         $viewdefs = $this->_viewdefs ;
116         $viewdefs [ 'panels' ] = $this->_convertToCanonicalForm ( $this->_viewdefs [ 'panels' ] , $this->_fielddefs ) ;
117         $this->implementation->save ( array ( self::$variableMap [ $this->_view ] => $viewdefs ) ) ;
118     }
119
120     /*
121      * Deploy the layout
122      * @param boolean $populate If true (default), then update the layout first with new layout information from the $_REQUEST array
123      */
124     function handleSave ($populate = true)
125     {
126         $GLOBALS [ 'log' ]->info ( get_class ( $this ) . "->handleSave()" ) ;
127
128         if ($populate)
129             $this->_populateFromRequest ( $this->_fielddefs ) ;
130
131         $viewdefs = $this->_viewdefs ;
132         $viewdefs [ 'panels' ] = $this->_convertToCanonicalForm ( $this->_viewdefs [ 'panels' ] , $this->_fielddefs ) ;
133         $this->implementation->deploy ( array ( self::$variableMap [ $this->_view ] => $viewdefs ) ) ;
134     }
135
136     /*
137      * Return the layout, padded out with (empty) and (filler) fields ready for display
138      */
139     function getLayout ()
140     {
141         $viewdefs = array () ;
142         $fielddefs = $this->_fielddefs;
143         $fielddefs [ $this->FILLER [ 'name' ] ] = $this->FILLER ;
144         $fielddefs [ MBConstants::$EMPTY [ 'name' ] ] = MBConstants::$EMPTY ;
145         
146                 foreach ( $this->_viewdefs [ 'panels' ] as $panelID => $panel )
147         {
148             foreach ( $panel as $rowID => $row )
149             {
150                 foreach ( $row as $colID => $fieldname )
151                 {
152                         if (isset ($this->_fielddefs [ $fieldname ]))
153                                         {
154                                                 $viewdefs [ $panelID ] [ $rowID ] [ $colID ] = self::_trimFieldDefs( $this->_fielddefs [ $fieldname ] ) ;
155                                         } 
156                                         else if (isset($this->_originalViewDef [ $fieldname ]) && is_array($this->_originalViewDef [ $fieldname ]))
157                                         {
158                                                 $viewdefs [ $panelID ] [ $rowID ] [ $colID ] = self::_trimFieldDefs( $this->_originalViewDef [ $fieldname ] ) ;
159                                         } 
160                                         else 
161                                         {
162                                                 $viewdefs [ $panelID ] [ $rowID ] [ $colID ] = array("name" => $fieldname, "label" => $fieldname);
163                                         }
164                 }
165             }
166         }
167         return $viewdefs ;
168     }
169
170     /*
171     * Return the tab definitions for tab/panel combo
172     */
173     function getTabDefs ()
174     {
175       $tabDefs = array();
176       $this->setUseTabs( false );
177       foreach ( $this->_viewdefs [ 'panels' ] as $panelID => $panel )
178       {
179
180         $tabDefs [ strtoupper($panelID) ] = array();
181
182         // panel or tab setting
183         if ( isset($this->_viewdefs [ 'templateMeta' ] [ 'tabDefs' ] [ strtoupper($panelID) ] [ 'newTab' ])
184         && is_bool($this->_viewdefs [ 'templateMeta' ] [ 'tabDefs' ] [ strtoupper($panelID) ] [ 'newTab' ]))
185         {
186           $tabDefs [ strtoupper($panelID) ] [ 'newTab' ] = $this->_viewdefs [ 'templateMeta' ] [ 'tabDefs' ] [ strtoupper($panelID) ] [ 'newTab' ];
187           if ($tabDefs [ strtoupper($panelID) ] [ 'newTab' ] == true)
188               $this->setUseTabs( true );
189         }
190         else
191         {
192           $tabDefs [ strtoupper($panelID) ] [ 'newTab' ] = false;
193         }
194
195         // collapsed panels
196         if ( isset($this->_viewdefs [ 'templateMeta' ] [ 'tabDefs' ] [ strtoupper($panelID) ] [ 'panelDefault' ])
197         && $this->_viewdefs [ 'templateMeta' ] [ 'tabDefs' ] [ strtoupper($panelID) ] [ 'panelDefault' ] == 'collapsed' )
198         {
199           $tabDefs [ strtoupper($panelID) ] [ 'panelDefault' ] = 'collapsed';
200         }
201         else
202         {
203           $tabDefs [ strtoupper($panelID) ] [ 'panelDefault' ] = 'expanded';
204         }
205       }
206       return $tabDefs;
207     }
208
209     /*
210      * Set tab definitions
211      */
212     function setTabDefs($tabDefs) {
213       $this->_viewdefs [ 'templateMeta' ] [ 'tabDefs' ] = $tabDefs;
214     }
215
216     function getMaxColumns ()
217     {
218         if (!empty( $this->_viewdefs) && isset($this->_viewdefs [ 'templateMeta' ] [ 'maxColumns' ]))
219                 {
220                         return $this->_viewdefs [ 'templateMeta' ] [ 'maxColumns' ] ;
221                 }else
222                 {
223                         return 2;
224                 }
225     }
226
227     function getAvailableFields ()
228     {
229
230         // Obtain the full list of valid fields in this module
231         $availableFields = array () ;
232         foreach ( $this->_fielddefs as $key => $def )
233         {
234             if ( GridLayoutMetaDataParser::validField ( $def,  $this->_view ) || isset($this->_originalViewDef[$key]) )
235             {
236                 //If the field original label existing, we should use the original label instead the label in its fielddefs.
237                 if(isset($this->_originalViewDef[$key]) && is_array($this->_originalViewDef[$key]) && isset($this->_originalViewDef[$key]['label'])){
238                     $availableFields [ $key ] = array ( 'name' => $key , 'label' => $this->_originalViewDef[$key]['label']) ; 
239                 }else{
240                     $availableFields [ $key ] = array ( 'name' => $key , 'label' => isset($def [ 'label' ]) ? $def [ 'label' ] : $def['vname'] ) ; // layouts use 'label' not 'vname' for the label entry
241                 }
242
243                 $availableFields[$key]['translatedLabel'] = translate( isset($def [ 'label' ]) ? $def [ 'label' ] : $def['vname'], $this->_moduleName);
244             }
245                         
246         }
247
248                 // Available fields are those that are in the Model and the original layout definition, but not already shown in the View
249         // 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
250         if (! empty ( $this->_viewdefs ))
251         {
252             foreach ( $this->_viewdefs [ 'panels' ] as $panel )
253             {
254                 foreach ( $panel as $row )
255                 {
256                     foreach ( $row as $field )
257                     {
258                         unset ( $availableFields [ $field ] ) ;
259                     }
260                 }
261             }
262         }
263         
264         //eggsurplus: Bug 10329 - sort on intuitive display labels
265         //sort by translatedLabel
266         function cmpLabel($a, $b) 
267         {
268             return strcmp($a["translatedLabel"], $b["translatedLabel"]);
269         }
270         usort($availableFields , 'cmpLabel');
271
272         return $availableFields ;
273     }
274
275     function getPanelDependency ( $panelID )
276     {
277         if ( ! isset ( $this->_viewdefs [ 'templateMeta' ][ 'dependency' ] ) && ! isset ( $this->_viewdefs [ 'templateMeta' ][ 'dependency' ] [ $panelID ] ) )
278                 return false;
279
280         return $this->_viewdefs  [ 'templateMeta' ][ 'dependency' ] [ $panelID ] ;
281     }
282
283     /*
284      * Add a new field to the layout
285      * If $panelID is passed in, attempt to add to that panel, otherwise add to the first panel
286      * The field is added in place of the first empty (not filler) slot after the last field in the panel; if that row is full, then a new row will be added to the end of the panel
287      * and the field added to the start of it.
288      * @param array $def Set of properties for the field, in same format as in the viewdefs
289      * @param string $panelID Identifier of the panel to add the field to; empty or false if we should use the first panel
290      */
291     function addField ( $def , $panelID = FALSE)
292     {
293
294         if (count ( $this->_viewdefs [ 'panels' ] ) == 0)
295         {
296             $GLOBALS [ 'log' ]->error ( get_class ( $this ) . "->addField(): _viewdefs empty for module {$this->_moduleName} and view {$this->_view}" ) ;
297         }
298
299         // if a panelID was not provided, use the first available panel in the list
300         if (! $panelID)
301         {
302             $panels = array_keys ( $this->_viewdefs [ 'panels' ] ) ;
303             list ( $dummy, $panelID ) = each ( $panels ) ;
304         }
305
306         if (isset ( $this->_viewdefs [ 'panels' ] [ $panelID ] ))
307         {
308
309             $panel = $this->_viewdefs [ 'panels' ] [ $panelID ] ;
310             $lastrow = count ( $panel ) - 1 ; // index starts at 0
311             $maxColumns = $this->getMaxColumns () ;
312             $lastRowDef = $this->_viewdefs [ 'panels' ] [ $panelID ] [ $lastrow ];
313             for ( $column = 0 ; $column < $maxColumns ; $column ++ )
314             {
315                 if (! isset ( $lastRowDef [ $column ] )
316                         || (is_array( $lastRowDef [ $column ]) && $lastRowDef [ $column ][ 'name' ] == '(empty)')
317                         || (is_string( $lastRowDef [ $column ]) && $lastRowDef [ $column ] == '(empty)')
318                 ){
319                     break ;
320                 }
321             }
322
323             // if we're on the last column of the last row, start a new row
324             if ($column >= $maxColumns)
325             {
326                 $lastrow ++ ;
327                 $this->_viewdefs [ 'panels' ] [ $panelID ] [ $lastrow ] = array ( ) ;
328                 $column = 0 ;
329             }
330
331             $this->_viewdefs [ 'panels' ] [ $panelID ] [ $lastrow ] [ $column ] = $def [ 'name' ] ;
332             // now update the fielddefs
333             if (isset($this->_fielddefs [ $def [ 'name' ] ]))
334             {
335                 $this->_fielddefs [ $def [ 'name' ] ] = array_merge ( $this->_fielddefs [ $def [ 'name' ] ] , $def ) ;
336             } else
337             {
338                 $this->_fielddefs [ $def [ 'name' ] ] = $def;
339             }
340         }
341         return true ;
342     }
343
344     /*
345      * Remove all instances of a field from the layout, and replace by (filler)
346      * Filler because we attempt to preserve the customized layout as much as possible - replacing by (empty) would mean that the positions or sizes of adjacent fields may change
347      * If the last row of a panel only consists of (filler) after removing the fields, then remove the row also. This undoes the standard addField() scenario;
348      * If the fields had been moved around in the layout however then this will not completely undo any addField()
349      * @param string $fieldName Name of the field to remove
350      * @return boolean True if the field was removed; false otherwise
351      */
352     function removeField ($fieldName)
353     {
354         $GLOBALS [ 'log' ]->info ( get_class ( $this ) . "->removeField($fieldName)" ) ;
355
356         $result = false ;
357         reset ( $this->_viewdefs ) ;
358         $firstPanel = each ( $this->_viewdefs [ 'panels' ] ) ;
359         $firstPanelID = $firstPanel [ 'key' ] ;
360
361         foreach ( $this->_viewdefs [ 'panels' ] as $panelID => $panel )
362         {
363             $lastRowTouched = false ;
364             $lastRowID = count ( $this->_viewdefs [ 'panels' ] [ $panelID ] ) - 1 ; // zero offset
365
366             foreach ( $panel as $rowID => $row )
367             {
368
369                 foreach ( $row as $colID => $field )
370                     if ($field == $fieldName)
371                     {
372                         $lastRowTouched = $rowID ;
373                         $this->_viewdefs [ 'panels' ] [ $panelID ] [ $rowID ] [ $colID ] = $this->FILLER [ 'name' ];
374                     }
375
376             }
377
378             // if we removed a field from the last row of this panel, tidy up if the last row now consists only of (empty) or (filler)
379
380             if ( $lastRowTouched ==  $lastRowID )
381             {
382                 $lastRow = $this->_viewdefs [ 'panels' ] [ $panelID ] [ $lastRowID ] ; // can't use 'end' for this as we need the key as well as the value...
383
384                 $empty = true ;
385
386                 foreach ( $lastRow as $colID => $field )
387                     $empty &=  $field == MBConstants::$EMPTY ['name' ] || $field == $this->FILLER [ 'name' ]  ;
388
389                 if ($empty)
390                 {
391                     unset ( $this->_viewdefs [ 'panels' ] [ $panelID ] [ $lastRowID ] ) ;
392                     // if the row was the only one in the panel, and the panel is not the first (default) panel, then remove the panel also
393                                         if ( count ( $this->_viewdefs [ 'panels' ] [ $panelID ] ) == 0 && $panelID != $firstPanelID )
394                                                 unset ( $this->_viewdefs [ 'panels' ] [ $panelID ] ) ;
395                 }
396
397             }
398
399             $result |= ($lastRowTouched !== false ); // explicitly compare to false as row 0 will otherwise evaluate as false
400         }
401
402         return $result ;
403
404     }
405
406     function setPanelDependency ( $panelID , $dependency )
407     {
408         // only accept dependencies for pre-existing panels
409         if ( ! isset ( $this->_viewdefs [ 'panels' ] [ $panelID ] ) )
410                 return false;
411
412         $this->_viewdefs  [ 'templateMeta' ] [ 'dependency' ] [ $panelID ] = $dependency ;
413         return true ;
414     }
415
416     /*
417      * Return an integer value for the next unused panel identifier, such that it and any larger numbers are guaranteed to be unused already in the layout
418      * Necessary when adding new panels to a layout
419      * @return integer First unique panel ID suffix
420      */
421     function getFirstNewPanelId ()
422     {
423         $firstNewPanelId = 0 ;
424         foreach ( $this->_viewdefs [ 'panels' ] as $panelID => $panel )
425         {
426             // strip out all but the numerics from the panelID - can't just use a cast as numbers may not be first in the string
427             for ( $i = 0, $result = '' ; $i < strlen ( $panelID ) ; $i ++ )
428             {
429                 if (is_numeric ( $panelID [ $i ] ))
430                 {
431                     $result .= $panelID [ $i ] ;
432                 }
433             }
434
435             $firstNewPanelId = max ( ( int ) $result, $firstNewPanelId ) ;
436         }
437         return $firstNewPanelId + 1 ;
438     }
439
440     /*
441      * Load the panel layout from the submitted form and update the _viewdefs
442      */
443     protected function _populateFromRequest ( &$fielddefs )
444     {
445         $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->populateFromRequest()" ) ;
446         $i = 1 ;
447
448         // set up the map of panel# (as provided in the _REQUEST) to panel ID (as used in $this->_viewdefs['panels'])
449         $i = 1 ;
450         foreach ( $this->_viewdefs [ 'panels' ] as $panelID => $panel )
451         {
452             $panelMap [ $i ++ ] = $panelID ;
453         }
454
455         foreach ( $_REQUEST as $key => $displayLabel )
456         {
457             $components = explode ( '-', $key ) ;
458             if ($components [ 0 ] == 'panel' && $components [ 2 ] == 'label')
459             {
460                 $panelMap [ $components [ '1' ] ] = $displayLabel ;
461             }
462         }
463
464         $this->_viewdefs [ 'panels' ] = array () ; // because the new field properties should replace the old fields, not be merged
465
466         // run through the $_REQUEST twice - first to obtain the fieldnames, the second to update the field properties
467         for ( $pass=1 ; $pass<=2 ; $pass++ )
468         {
469                 foreach ( $_REQUEST as $slot => $value )
470                 {
471                 $slotComponents = explode ( '-', $slot ) ; // [0] = 'slot', [1] = panel #, [2] = slot #, [3] = property name
472
473                 if ($slotComponents [ 0 ] == 'slot')
474                 {
475                         $slotNumber = $slotComponents [ '2' ] ;
476                         $panelID = $panelMap [ $slotComponents [ '1' ] ] ;
477                         $rowID = floor ( $slotNumber / $this->getMaxColumns () ) ;
478                         $colID = $slotNumber - ($rowID * $this->getMaxColumns ()) ;
479                         $property = $slotComponents [ '3' ] ;
480
481                         //If this field has a custom definition, copy that over
482                         if ( $pass == 1 )
483                         {
484                                 if ( $property == 'name' )
485                                 $this->_viewdefs [ 'panels' ] [ $panelID ] [ $rowID ] [ $colID ] = $value ;
486                         } else
487                         {
488                                 // update fielddefs for this property in the provided position
489                                 if ( isset ( $this->_viewdefs [ 'panels' ] [ $panelID ] [ $rowID ] [ $colID ] ) )
490                                 {
491                                         $fieldname = $this->_viewdefs [ 'panels' ] [ $panelID ] [ $rowID ] [ $colID ] ;
492                                                         $fielddefs [ $fieldname ] [ $property ] = $value ;
493                                 }
494                         }
495                 }
496
497                 }
498         }
499
500 /*
501         //Set the tabs setting
502         if (isset($_REQUEST['panels_as_tabs']))
503         {
504                 if ($_REQUEST['panels_as_tabs'] == false || $_REQUEST['panels_as_tabs'] == "false")
505                    $this->setUseTabs( false );
506                 else
507                    $this->setUseTabs( true );
508         }
509 */
510
511         //Set the tab definitions
512         $tabDefs = array();
513         $this->setUseTabs( false );
514         foreach ( $this->_viewdefs [ 'panels' ] as $panelID => $panel )
515         {
516           // panel or tab setting
517           $tabDefs [ strtoupper($panelID) ] = array();
518           if ( isset($_REQUEST['tabDefs_'.$panelID.'_newTab']) )
519           {
520             $tabDefs [ strtoupper($panelID) ] [ 'newTab' ] = ( $_REQUEST['tabDefs_'.$panelID.'_newTab'] == '1' ) ? true : false;
521             if ($tabDefs [ strtoupper($panelID) ] [ 'newTab' ] == true)
522                 $this->setUseTabs( true );
523           }
524           else
525           {
526             $tabDefs [ strtoupper($panelID) ] [ 'newTab' ] = false;
527           }
528
529           // collapse panel
530           if ( isset($_REQUEST['tabDefs_'.$panelID.'_panelDefault']) )
531           {
532             $tabDefs [ strtoupper($panelID) ] [ 'panelDefault' ] = ( $_REQUEST['tabDefs_'.$panelID.'_panelDefault'] == 'collapsed' ) ? 'collapsed' : 'expanded';
533           }
534           else
535           {
536             $tabDefs [ strtoupper($panelID) ] [ 'panelDefault' ] = 'expanded';
537           }
538
539         }
540         $this->setTabDefs($tabDefs);
541
542         //bug: 38232 - Set the sync detail and editview settings
543         if (isset($_REQUEST['sync_detail_and_edit']))
544         {
545                 if ($_REQUEST['sync_detail_and_edit'] === false || $_REQUEST['sync_detail_and_edit'] === "false")
546             {
547                    $this->setSyncDetailEditViews( false );
548             }
549             elseif(!empty($_REQUEST['sync_detail_and_edit']))
550             {
551                    $this->setSyncDetailEditViews( true );
552             }
553         }
554
555         $GLOBALS [ 'log' ]->debug ( print_r ( $this->_viewdefs [ 'panels' ], true ) ) ;
556
557     }
558
559     /*  Convert our internal format back to the standard Canonical MetaData layout
560      *  First non-(empty) field goes in at column 0; all other (empty)'s removed
561      *  Studio required fields are also added to the layout.
562      *  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
563      */
564     protected function _convertToCanonicalForm ( $panels , $fielddefs )
565     {
566         $previousViewDef = $this->getFieldsFromLayout($this->implementation->getViewdefs ());
567         $oldDefs = $this->implementation->getViewdefs ();
568         $currentFields = $this->getFieldsFromLayout($this->_viewdefs);
569         foreach($fielddefs as $field => $def)
570         {
571                 if (self::fieldIsRequired($def) && !isset($currentFields[$field]))
572                 {
573                 //Use the previous viewdef if this field was on it.
574                 if (isset($previousViewDef[$field]))
575                 {
576                     $def = $previousViewDef[$field];
577                 }
578                 //next see if the field was on the original layout.
579                 else if (isset ($this->_originalViewDef [ $field ]))
580                 {
581                     $def = $this->_originalViewDef [ $field ] ;   
582                 }
583                 //Otherwise make up a viewdef for it from field_defs
584                 else
585                 {
586                     $def =  self::_trimFieldDefs( $def ) ;
587                 }
588                 $this->addField($def);
589                 }
590         }
591         
592         foreach ( $panels as $panelID => $panel )
593         {
594             // remove all (empty)s
595             foreach ( $panel as $rowID => $row )
596             {
597                 $startOfRow = true ;
598                 $offset = 0 ;
599                 foreach ( $row as $colID => $fieldname )
600                 {
601                     if ($fieldname == MBConstants::$EMPTY[ 'name' ])
602                     {
603                         // if a leading (empty) then remove (by noting that remaining fields need to be shuffled along)
604                         if ($startOfRow)
605                         {
606                             $offset ++ ;
607                         }
608                         unset ( $row [ $colID ] ) ;
609                     } else
610                     {
611                         $startOfRow = false ;
612                     }
613                 }
614
615                 // reindex to remove leading (empty)s and replace fieldnames by full definition from fielddefs
616                 $newRow = array ( ) ;
617                 foreach ( $row as $colID => $fieldname )
618                 {
619                         if ($fieldname == null )
620                            continue;
621                     //Backwards compatibility and a safeguard against multiple calls to _convertToCanonicalForm
622                     if(is_array($fieldname))
623                     {
624
625                         $newRow [ $colID - $offset ] = $fieldname;
626                         continue;
627                     }else if(!isset($fielddefs[$fieldname])){
628                        continue;
629                      }
630
631                         //Replace (filler) with the empty string
632                         if ($fieldname == $this->FILLER[ 'name' ]) {
633                         $newRow [ $colID - $offset ] = '' ;
634                     }
635                     //Use the previous viewdef if this field was on it.
636                                         else if (isset($previousViewDef[$fieldname]))
637                         {
638                         $newRow[$colID - $offset] = $this->getNewRowItem($previousViewDef[$fieldname], $fielddefs[$fieldname]);
639                         }
640                     //next see if the field was on the original layout.
641                     else if (isset ($this->_originalViewDef [ $fieldname ]))
642                     {
643                         $newRow[$colID - $offset] = $this->getNewRowItem($this->_originalViewDef[$fieldname], $fielddefs[$fieldname]);  
644                     }
645                         //Otherwise make up a viewdef for it from field_defs
646                         else if (isset ($fielddefs [ $fieldname ]))
647                         {
648                                 $newRow [ $colID - $offset ] =  self::_trimFieldDefs( $fielddefs [ $fieldname ] ) ;
649                                 
650                         }
651                         //No additional info on this field can be found, jsut use the name;
652                         else 
653                         {
654                         $newRow [ $colID - $offset ] = $fieldname;
655                         }
656                 }
657                 $panels [ $panelID ] [ $rowID ] = $newRow ;
658             }
659         }
660         
661         return $panels ;
662     }
663
664     /*
665      * fixing bug #44428: Studio | Tab Order causes layout errors
666      * @param string|array $source it can be a string which contain just a name of field 
667      *                                  or an array with field attributes including name
668      * @param array $fielddef stores field defs from request
669      * @return string|array definition of new row item
670      */
671     function getNewRowItem($source, $fielddef)
672     {
673         //We should copy over the tabindex if it is set.
674         $newRow = array();
675         if (isset ($fielddef) && !empty($fielddef['tabindex']))
676         {
677             if (is_array($source))
678             {
679                 $newRow = $source;
680             }
681             else
682             {
683                 $newRow['name'] = $source;
684             }
685             $newRow['tabindex'] = $fielddef['tabindex'];
686         }
687         else
688         {
689             $newRow = $source;
690         }
691         return $newRow;
692     }
693
694     /*
695      * Convert from the standard MetaData format to our internal format
696      * Replace NULL with (filler) and missing entries with (empty)
697      */
698     protected function _convertFromCanonicalForm ( $panels , $fielddefs )
699     {
700         if (empty ( $panels ))
701             return ;
702
703         // Fix for a flexibility in the format of the panel sections - if only one panel, then we don't have a panel level defined,
704                 // it goes straight into rows
705         // See EditView2 for similar treatment
706         if (! empty ( $panels ) && count ( $panels ) > 0)
707         {
708             $keys = array_keys ( $panels ) ;
709             if (is_numeric ( $keys [ 0 ] ))
710             {
711                 $defaultPanel = $panels ;
712                 unset ( $panels ) ; //blow away current value
713                 $panels [ 'default' ] = $defaultPanel ;
714             }
715         }
716
717         $newPanels = array ( ) ;
718
719         // replace '' with (filler)
720         foreach ( $panels as $panelID => $panel )
721         {
722             foreach ( $panel as $rowID => $row )
723             {
724                 $cols = 0;
725                 foreach ( $row as $colID => $col )
726                 {
727                     if ( ! empty ( $col ) )
728                     {
729                         if ( is_string ( $col ))
730                         {
731                             $fieldname = $col ;
732                         } else if (! empty ( $col [ 'name' ] ))
733                         {
734                             $fieldname = $col [ 'name' ] ;
735                         }
736                     } else
737                     {
738                         $fieldname = $this->FILLER['name'] ;
739                     }
740
741                     $newPanels [ $panelID ] [ $rowID ] [ $cols ] = $fieldname ;
742                     $cols++;
743                 }
744             }
745         }
746
747         // replace missing fields with (empty)
748         foreach ( $newPanels as $panelID => $panel )
749         {
750             $column = 0 ;
751             foreach ( $panel as $rowID => $row )
752             {
753                 // pad between fields on a row
754                 foreach ( $row as $colID => $col )
755                 {
756                     for ( $i = $column + 1 ; $i < $colID ; $i ++ )
757                     {
758                         $row [ $i ] = MBConstants::$EMPTY ['name'];
759                     }
760                     $column = $colID ;
761                 }
762                 // now pad out to the end of the row
763                 if (($column + 1) < $this->getMaxColumns ())
764                 { // last column is maxColumns-1
765                     for ( $i = $column + 1 ; $i < $this->getMaxColumns () ; $i ++ )
766                     {
767                         $row [ $i ] = MBConstants::$EMPTY ['name'] ;
768                     }
769                 }
770                 ksort ( $row ) ;
771                 $newPanels [ $panelID ] [ $rowID ] = $row ;
772             }
773         }
774
775         return $newPanels ;
776     }
777     
778     protected function getFieldsFromLayout($viewdef) {
779         if (isset($viewdef['panels']))
780         {
781                 $panels = $viewdef['panels']; 
782         } else {
783             $panels = $viewdef[self::$variableMap [ $this->_view ] ]['panels'];
784         }
785         
786         $ret = array();
787         if (is_array($panels)) 
788         {       
789                 foreach ( $panels as $rows) {
790                     foreach ($rows as $fields) {
791                         if (!is_array($fields)) {
792                                 $ret[$fields] = $fields;
793                                 continue;
794                         }
795                         foreach ($fields as $field) {
796                             if (is_array($field) && !empty($field['name']))
797                             {
798                                 $ret[$field['name']] = $field;  
799                             }
800                             else if(!is_array($field)){
801                             $ret[$field] = $field;
802                         }                           
803                         }
804                     }
805                 }
806         }
807         return $ret;
808     }
809     
810     protected function fieldIsRequired($def)
811     {
812         if (isset($def['studio']))
813         { 
814                 if (is_array($def['studio']))
815                 {
816                         if (!empty($def['studio'][$this->_view]) && $def['studio'][$this->_view] == "required")
817                         {
818                                 return true;
819     }
820                         else if (!empty($def['studio']['required']) && $def['studio']['required'] == true)
821                         {
822                                 return true;
823                         }
824                 }
825                 else if ($def['studio'] == "required" ){
826                   return true;
827                 }
828     }
829         return false;
830     }
831
832     static function _trimFieldDefs ( $def )
833         {
834                 $ret = array_intersect_key ( $def , 
835             array ( 'studio' => true , 'name' => true , 'label' => true , 'displayParams' => true , 'comment' => true , 
836                     'customCode' => true , 'customLabel' => true , 'tabindex' => true , 'hideLabel' => true) ) ;
837         if (!empty($def['vname']) && empty($def['label']))
838             $ret['label'] = $def['vname'];
839                 return $ret;
840         }
841         
842         public function getUseTabs(){
843         if (isset($this->_viewdefs  [ 'templateMeta' ]['useTabs']))
844            return $this->_viewdefs  [ 'templateMeta' ]['useTabs'];
845            
846         return false;
847     }
848     
849     public function setUseTabs($useTabs){
850         $this->_viewdefs  [ 'templateMeta' ]['useTabs'] = $useTabs;
851     }
852     
853     /**
854      * Return whether the Detail & EditView should be in sync.
855      */
856         public function getSyncDetailEditViews(){
857         if (isset($this->_viewdefs  [ 'templateMeta' ]['syncDetailEditViews']))
858            return $this->_viewdefs  [ 'templateMeta' ]['syncDetailEditViews'];
859            
860         return false;
861     }
862     
863     /**
864      * Sync DetailView & EditView. This should only be set on the EditView
865      * @param bool $syncViews
866      */
867     public function setSyncDetailEditViews($syncDetailEditViews){
868         $this->_viewdefs  [ 'templateMeta' ]['syncDetailEditViews'] = $syncDetailEditViews;
869     }
870
871     /**
872      * Getter function to get the implementation method which is a private variable
873      * @return DeployedMetaDataImplementation
874      */
875     public function getImplementation(){
876         return $this->implementation;
877     }
878
879     /**
880      * Public access to _convertFromCanonicalForm
881      * @param  $panels
882      * @param  $fielddefs
883      * @return array
884      */
885     public function convertFromCanonicalForm ( $panels , $fielddefs )
886     {
887         return $this->_convertFromCanonicalForm ( $panels , $fielddefs );
888     }
889
890      /**
891      * Public access to _convertToCanonicalForm
892      * @param  $panels
893      * @param  $fielddefs
894      * @return array
895      */
896     public function convertToCanonicalForm ( $panels , $fielddefs )
897     {
898         return $this->_convertToCanonicalForm ( $panels , $fielddefs );
899     }
900
901     
902     /**
903      * @return Array list of fields in this module that have the calculated property
904      */
905     public function getCalculatedFields() {
906         $ret = array();
907         foreach ($this->_fielddefs as $field => $def)
908         {
909             if(!empty($def['calculated']) && !empty($def['formula']))
910             {
911                 $ret[] = $field;
912             }
913         }
914         
915         return $ret;
916     }
917
918     /**
919      * @return Array fields in the given panel
920      */
921     public function getFieldsInPanel($targetPanel) {
922         return iterator_to_array(new RecursiveIteratorIterator(new RecursiveArrayIterator($this->_viewdefs['panels'][$targetPanel])));
923     }
924 }
925
926 ?>