]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - modules/ModuleBuilder/parsers/views/DeployedMetaDataImplementation.php
Release 6.5.0
[Github/sugarcrm.git] / modules / ModuleBuilder / parsers / views / DeployedMetaDataImplementation.php
1 <?php
2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4  * SugarCRM Community Edition is a customer relationship management program developed by
5  * SugarCRM, Inc. Copyright (C) 2004-2012 SugarCRM Inc.
6  * 
7  * This program is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU Affero General Public License version 3 as published by the
9  * Free Software Foundation with the addition of the following permission added
10  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11  * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
13  * 
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
17  * details.
18  * 
19  * You should have received a copy of the GNU Affero General Public License along with
20  * this program; if not, see http://www.gnu.org/licenses or write to the Free
21  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22  * 02110-1301 USA.
23  * 
24  * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25  * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
26  * 
27  * The interactive user interfaces in modified source and object code versions
28  * of this program must display Appropriate Legal Notices, as required under
29  * Section 5 of the GNU Affero General Public License version 3.
30  * 
31  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32  * these Appropriate Legal Notices must retain the display of the "Powered by
33  * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34  * technical reasons, the Appropriate Legal Notices must display the words
35  * "Powered by SugarCRM".
36  ********************************************************************************/
37
38 /*
39  * Implementation class (following a Bridge Pattern) for handling loading and saving deployed module metadata
40  * For example, listview or editview viewdefs
41  */
42
43 require_once 'modules/ModuleBuilder/parsers/views/AbstractMetaDataImplementation.php' ;
44 require_once 'modules/ModuleBuilder/parsers/views/MetaDataImplementationInterface.php' ;
45 require_once 'modules/ModuleBuilder/parsers/views/ListLayoutMetaDataParser.php' ;
46 require_once 'modules/ModuleBuilder/parsers/views/GridLayoutMetaDataParser.php' ;
47 require_once 'modules/ModuleBuilder/parsers/views/PopupMetaDataParser.php' ;
48 require_once 'modules/ModuleBuilder/Module/StudioModuleFactory.php' ;
49 require_once 'modules/ModuleBuilder/parsers/constants.php' ;
50
51 class DeployedMetaDataImplementation extends AbstractMetaDataImplementation implements MetaDataImplementationInterface
52 {
53
54         /*
55          * Constructor
56          * @param string $view
57          * @param string $moduleName
58          * @throws Exception Thrown if the provided view doesn't exist for this module
59          */
60         function __construct ($view , $moduleName)
61         {
62
63                 // BEGIN ASSERTIONS
64                 if (! isset ( $GLOBALS [ 'beanList' ] [ $moduleName ] ))
65                 {
66                         sugar_die ( get_class ( $this ) . ": Modulename $moduleName is not a Deployed Module" ) ;
67                 }
68                 // END ASSERTIONS
69
70                 $this->_view = strtolower ( $view ) ;
71                 $this->_moduleName = $moduleName ;
72
73                 $module = StudioModuleFactory::getStudioModule( $moduleName ) ;
74                 $this->module_dir = $module->seed->module_dir;
75                 $fielddefs = $module->getFields();
76
77         //Load any custom views
78         $sm = StudioModuleFactory::getStudioModule($moduleName);
79         foreach($sm->sources as $file => $def)
80         {
81             if (!empty($def['view'])) {
82                 $viewVar = "viewdefs";
83                 if (!empty($def['type']) && !empty($this->_fileVariables[$def["type"]]))
84                     $viewVar = $this->_fileVariables[$def["type"]];
85                 $this->_fileVariables[$def['view']] = $viewVar;
86             }
87         }
88
89                 $loaded = null ;
90                 foreach ( array ( MB_BASEMETADATALOCATION , MB_CUSTOMMETADATALOCATION , MB_WORKINGMETADATALOCATION , MB_HISTORYMETADATALOCATION ) as $type )
91                 {
92                         $this->_sourceFilename = $this->getFileName ( $view, $moduleName, $type ) ;
93                         if($view == MB_POPUPSEARCH || $view == MB_POPUPLIST){
94                                 global $current_language;
95                                 $mod = return_module_language($current_language , $moduleName);
96                                 $layout = $this->_loadFromPopupFile ( $this->_sourceFilename , $mod, $view);
97                         }else{
98                                 $layout = $this->_loadFromFile ( $this->_sourceFilename );
99                         }
100                         if ( null !== $layout )
101                         {
102                                 // merge in the fielddefs from this layout
103                                 $this->_mergeFielddefs ( $fielddefs , $layout ) ;
104                                 $loaded = $layout ;
105                         }
106                 }
107
108                 if ($loaded === null)
109                 {
110                         switch ( $view )
111                         {
112                                 case MB_QUICKCREATE:
113                                         // Special handling for QuickCreates - if we don't have a QuickCreate definition in the usual places, then use an EditView
114
115                                         $loaded = $this->_loadFromFile ( $this->getFileName ( MB_EDITVIEW, $this->_moduleName, MB_BASEMETADATALOCATION ) ) ;
116
117                                         if ($loaded === null)
118                                                 throw new Exception( get_class ( $this ) . ": cannot convert from EditView to QuickCreate for Module $this->_moduleName - definitions for EditView are missing" ) ;
119
120                                         // Now change the array index
121                                         $temp = $loaded [ GridLayoutMetaDataParser::$variableMap [ MB_EDITVIEW ] ] ;
122                                         unset ( $loaded [ GridLayoutMetaDataParser::$variableMap [ MB_EDITVIEW ] ] ) ;
123                                         $loaded [ GridLayoutMetaDataParser::$variableMap [ MB_QUICKCREATE ] ] = $temp ;
124                                         // finally, save out our new definition so that we have a base record for the history to work from
125                                         $this->_sourceFilename = self::getFileName ( MB_QUICKCREATE, $this->_moduleName, MB_CUSTOMMETADATALOCATION ) ;
126                                         $this->_saveToFile ( $this->_sourceFilename, $loaded ) ;
127                                         $this->_mergeFielddefs ( $fielddefs , $loaded ) ;
128                                         break;
129
130                                 case MB_DASHLETSEARCH:
131                         case MB_DASHLET:
132                                 $type = $module->getType () ;
133                                 $this->_sourceFilename = self::getFileName ( $view, $moduleName, MB_CUSTOMMETADATALOCATION ) ;
134                                 $needSave = false;
135                                 if(file_exists( "custom/modules/{$moduleName}/metadata/".basename ( $this->_sourceFilename))){
136                                         $loaded = $this->_loadFromFile ( "custom/modules/{$moduleName}/metadata/".basename ( $this->_sourceFilename) )  ;         
137                                 }
138                                 elseif(file_exists(
139                                         "modules/{$moduleName}/Dashlets/My{$moduleName}Dashlet/My{$moduleName}Dashlet.data.php")){
140                                         $loaded = $this->_loadFromFile ( "modules/{$moduleName}/Dashlets/My{$moduleName}Dashlet/My{$moduleName}Dashlet.data.php");
141                                 }
142                                 else{
143                                         $loaded = $this->_loadFromFile ( "include/SugarObjects/templates/$type/metadata/".basename ( $this->_sourceFilename ) ) ;
144                                         $needSave = true;                       
145                                 }
146                                 if ($loaded === null)
147                                                 throw new Exception( get_class ( $this ) . ": cannot create dashlet view for module $moduleName - definitions for $view are missing in the SugarObject template for type $type" ) ;
148                                 $loaded = $this->replaceVariables($loaded, $module);
149                                 $temp = $this->_moduleName;
150                                 if($needSave){
151                                         $this->_moduleName = $this->_moduleName.'Dashlet';
152                                                 $this->_saveToFile ( $this->_sourceFilename, $loaded,false) ; // write out without the placeholder module_name and object
153                                                 $this->_moduleName = $temp;
154                                                 unset($temp);
155                                 }
156                                         $this->_mergeFielddefs ( $fielddefs , $loaded ) ;
157                                         break;
158                                 case MB_POPUPLIST:
159                         case MB_POPUPSEARCH:
160                                 $type = $module->getType () ;
161                                         $this->_sourceFilename = self::getFileName ( $view, $moduleName, MB_CUSTOMMETADATALOCATION ) ;
162
163                                         // Now we can copy the wireless view from the template
164                                         global $current_language;
165                                         $mod = return_module_language($current_language , $moduleName);
166                                         $loadedForWrite = $this->_loadFromPopupFile (  "include/SugarObjects/templates/$type/metadata/".basename ( $this->_sourceFilename )  , $mod, $view, true);
167                                 if ($loadedForWrite === null)
168                                                 throw new Exception( get_class ( $this ) . ": cannot create popup view for module $moduleName - definitions for $view are missing in the SugarObject template for type $type" ) ;
169                                 $loadedForWrite = $this->replaceVariables($loadedForWrite, $module);
170                                         $this->_saveToFile ( $this->_sourceFilename, $loadedForWrite , false , true) ; // write out without the placeholder module_name and object
171                                         $loaded = $this->_loadFromPopupFile (  "include/SugarObjects/templates/$type/metadata/".basename ( $this->_sourceFilename )  , $mod, $view);
172                                         $this->_mergeFielddefs ( $fielddefs , $loaded ) ;
173                                 break;
174                                 default:
175
176                         }
177                         if ( $loaded === null )
178                                 throw new Exception( get_class ( $this ) . ": view definitions for View $this->_view and Module $this->_moduleName are missing" ) ;
179                 }
180
181                 $this->_viewdefs = $loaded ;
182                 // Set the original Viewdefs - required to ensure we don't lose fields from the base layout
183                 // Check the base location first, then if nothing is there (which for example, will be the case for some QuickCreates, and some mobile layouts - see above)
184                 // we need to check the custom location where the derived layouts will be
185                 foreach ( array ( MB_BASEMETADATALOCATION , MB_CUSTOMMETADATALOCATION ) as $type )
186                 {
187                         $sourceFilename = $this->getFileName ( $view, $moduleName, $type ) ;
188                         if($view == MB_POPUPSEARCH || $view == MB_POPUPLIST){
189                                 global $current_language;
190                                 $mod = return_module_language($current_language , $moduleName);
191                                 $layout = $this->_loadFromPopupFile ( $sourceFilename , $mod, $view);
192                         }else{
193                                 $layout = $this->_loadFromFile ( $sourceFilename );
194                         }
195                         if ( null !== ($layout ) )
196                         {
197                                 $this->_originalViewdefs = $layout ;
198                                 break ;
199                         }
200                 }
201                 //For quick create viewdefs, if there is no quickcreatedefs.php under MB_BASEMETADATALOCATION, the original defs is editview defs.
202         if ($view == MB_QUICKCREATE) {
203           foreach(array(MB_QUICKCREATE, MB_EDITVIEW) as $v){
204             $sourceFilename = $this->getFileName($v, $moduleName, MB_BASEMETADATALOCATION ) ;
205             if (file_exists($sourceFilename )) {
206               $layout = $this->_loadFromFile($sourceFilename );
207               if (null !== $layout && isset($layout[GridLayoutMetaDataParser::$variableMap[$v]])) {
208                 $layout = array(GridLayoutMetaDataParser::$variableMap[MB_QUICKCREATE] => $layout[GridLayoutMetaDataParser::$variableMap[$v]]);
209                 break;
210               }
211             }
212           }
213           
214           if (null === $layout) {
215             $sourceFilename = $this->getFileName($view, $moduleName, MB_CUSTOMMETADATALOCATION );
216             $layout = $this->_loadFromFile($sourceFilename );
217           }
218           
219           if (null !== $layout  ) {
220             $this->_originalViewdefs = $layout ;
221           }
222         }
223         
224                 $this->_fielddefs = $fielddefs ;
225                 $this->_history = new History ( $this->getFileName ( $view, $moduleName, MB_HISTORYMETADATALOCATION ) ) ;
226
227         }
228
229         function getLanguage ()
230         {
231                 return $this->_moduleName ;
232         }
233         
234         function getOriginalViewdefs()
235         {
236                 return $this->_originalViewdefs;
237         }
238
239
240         /*
241          * Save a draft layout
242          * @param array defs    Layout definition in the same format as received by the constructor
243          */
244         function save ($defs)
245         {
246                 //If we are pulling from the History Location, that means we did a restore, and we need to save the history for the previous file.
247                 if ($this->_sourceFilename == $this->getFileName ( $this->_view, $this->_moduleName, MB_HISTORYMETADATALOCATION )) {
248                         foreach ( array ( MB_WORKINGMETADATALOCATION , MB_CUSTOMMETADATALOCATION , MB_BASEMETADATALOCATION ) as $type ) {
249                                 if (file_exists($this->getFileName ( $this->_view, $this->_moduleName, $type ))) {
250                                         $this->_history->append ( $this->getFileName ( $this->_view, $this->_moduleName, $type )) ;
251                                         break;
252                                 }
253                         }
254                 } else {
255                         $this->_history->append ( $this->_sourceFilename ) ;
256                 }
257
258                 $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->save(): writing to " . $this->getFileName ( $this->_view, $this->_moduleName, MB_WORKINGMETADATALOCATION ) ) ;
259                 $this->_saveToFile ( $this->getFileName ( $this->_view, $this->_moduleName, MB_WORKINGMETADATALOCATION ), $defs ) ;
260         }
261
262         /*
263          * Deploy a layout
264          * @param array defs    Layout definition in the same format as received by the constructor
265          */
266         function deploy ($defs)
267         {
268                 if ($this->_sourceFilename == $this->getFileName ( $this->_view, $this->_moduleName, MB_HISTORYMETADATALOCATION )) {
269                         foreach ( array ( MB_WORKINGMETADATALOCATION , MB_CUSTOMMETADATALOCATION , MB_BASEMETADATALOCATION ) as $type ) {
270                                 if (file_exists($this->getFileName ( $this->_view, $this->_moduleName, $type ))) {
271                                         $this->_history->append ( $this->getFileName ( $this->_view, $this->_moduleName, $type )) ;
272                                         break;
273                                 }
274                         }
275                 } else {
276                         $this->_history->append ( $this->_sourceFilename ) ;
277                 }
278                 // when we deploy get rid of the working file; we have the changes in the MB_CUSTOMMETADATALOCATION so no need for a redundant copy in MB_WORKINGMETADATALOCATION
279                 // this also simplifies manual editing of layouts. You can now switch back and forth between Studio and manual changes without having to keep these two locations in sync
280                 $workingFilename = $this->getFileName ( $this->_view, $this->_moduleName, MB_WORKINGMETADATALOCATION ) ;
281
282                 if (file_exists ( $workingFilename ))
283                 unlink ( $this->getFileName ( $this->_view, $this->_moduleName, MB_WORKINGMETADATALOCATION ) ) ;
284                 $filename = $this->getFileName ( $this->_view, $this->_moduleName, MB_CUSTOMMETADATALOCATION ) ;
285                 $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->deploy(): writing to " . $filename ) ;
286                 $this->_saveToFile ( $filename, $defs ) ;
287
288                 // now clear the cache so that the results are immediately visible
289                 include_once ('include/TemplateHandler/TemplateHandler.php') ;
290                 TemplateHandler::clearCache ( $this->_moduleName ) ;
291         }
292
293         /*
294          * Construct a full pathname for the requested metadata
295          * Can be called statically
296          * @param string view           The view type, that is, EditView, DetailView etc
297          * @param string modulename     The name of the module that will use this layout
298          * @param string type
299          */
300         public static function getFileName ($view , $moduleName , $type = MB_CUSTOMMETADATALOCATION)
301         {
302
303                 $pathMap = array (
304             MB_BASEMETADATALOCATION => '' ,
305             MB_CUSTOMMETADATALOCATION => 'custom/' ,
306             MB_WORKINGMETADATALOCATION => 'custom/working/' ,
307             MB_HISTORYMETADATALOCATION => 'custom/history/'
308         ) ;
309                 $type = strtolower ( $type ) ;
310
311                 $filenames = array (
312             MB_DASHLETSEARCH => 'dashletviewdefs',
313             MB_DASHLET => 'dashletviewdefs',
314             MB_POPUPSEARCH => 'popupdefs',
315             MB_POPUPLIST => 'popupdefs',
316                         MB_LISTVIEW => 'listviewdefs' ,
317                         MB_BASICSEARCH => 'searchdefs' ,
318                         MB_ADVANCEDSEARCH => 'searchdefs' ,
319                         MB_EDITVIEW => 'editviewdefs' ,
320                         MB_DETAILVIEW => 'detailviewdefs' ,
321                         MB_QUICKCREATE => 'quickcreatedefs',
322                 ) ;
323
324         //In a deployed module, we can check for a studio module with file name overrides.
325         $sm = StudioModuleFactory::getStudioModule($moduleName);
326         foreach($sm->sources as $file => $def)
327         {
328             if (!empty($def['view'])) {
329                 $filenames[$def['view']] = substr($file, 0, strlen($file) - 4);
330             }
331
332         }
333
334                 // BEGIN ASSERTIONS
335                 if (! isset ( $pathMap [ $type ] ))
336                 {
337                         sugar_die ( "DeployedMetaDataImplementation->getFileName(): Type $type is not recognized" ) ;
338                 }
339                 if (! isset ( $filenames [ $view ] ))
340         {
341             sugar_die ( "DeployedMetaDataImplementation->getFileName(): View $view is not recognized" ) ;
342         }
343                 // END ASSERTIONS
344
345                 
346
347                 // Construct filename
348                 return $pathMap [ $type ] . 'modules/' . $moduleName . '/metadata/' . $filenames [ $view ] . '.php' ;
349         }
350         
351         private function replaceVariables($defs, $module) {
352                 $var_values = array(
353                         "<object_name>" => $module->seed->object_name, 
354                         "<_object_name>" => strtolower($module->seed->object_name),  
355                         "<OBJECT_NAME>" => strtoupper($module->seed->object_name), 
356                         "<module_name>" => $module->seed->module_dir,  
357                         '<_module_name>'=> strtolower ( $module->seed->module_dir ) 
358                 );
359                 return $this->recursiveVariableReplace($defs, $module, $var_values);
360         }
361
362         public function getModuleDir(){
363                 return $this->module_dir;
364         }
365     
366     private function recursiveVariableReplace($arr, $module, $replacements) {
367         $ret = array();
368                 foreach ($arr as $key => $val) {
369                         if (is_array($val)) {
370                     $newkey = $key;
371                 $val = $this->recursiveVariableReplace($val, $module, $replacements);
372                     foreach ($replacements as $var => $rep) {
373                         $newkey = str_replace($var, $rep, $newkey);
374                     }
375                                 $ret[$newkey] = $val;
376                 } else {
377                 $newkey = $key;
378                             $newval = $val;
379                 if(is_string($val))
380                 {
381                     foreach ($replacements as $var => $rep) {
382                         $newkey = str_replace($var, $rep, $newkey);
383                         $newval = str_replace($var, $rep, $newval);
384                     }
385                 }
386                 $ret[$newkey] = $newval;
387                         }
388         }
389                 return $ret;
390         }
391
392     /**
393      * This is just a wrapper to the private method _saveToFile
394      * @param  $file    the file name to save to
395      * @param  $defs    the defs to save to the file
396      * @return void
397      */
398     public function saveToFile($file, $defs)
399     {
400         $this->_saveToFile ( $file, $defs ) ;
401     }
402 }