]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - modules/ModuleBuilder/parsers/views/DeployedMetaDataImplementation.php
Release 6.5.6
[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                                         global $current_language;
164                                         $mod = return_module_language($current_language , $moduleName);
165                                         $loadedForWrite = $this->_loadFromPopupFile (  "include/SugarObjects/templates/$type/metadata/".basename ( $this->_sourceFilename )  , $mod, $view, true);
166                                 if ($loadedForWrite === null)
167                                                 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" ) ;
168                                 $loadedForWrite = $this->replaceVariables($loadedForWrite, $module);
169                                         $this->_saveToFile ( $this->_sourceFilename, $loadedForWrite , false , true) ; // write out without the placeholder module_name and object
170                                         $loaded = $this->_loadFromPopupFile (  "include/SugarObjects/templates/$type/metadata/".basename ( $this->_sourceFilename )  , $mod, $view);
171                                         $this->_mergeFielddefs ( $fielddefs , $loaded ) ;
172                                 break;
173                                 default:
174
175                         }
176                         if ( $loaded === null )
177                                 throw new Exception( get_class ( $this ) . ": view definitions for View $this->_view and Module $this->_moduleName are missing" ) ;
178                 }
179
180                 $this->_viewdefs = $loaded ;
181                 // Set the original Viewdefs - required to ensure we don't lose fields from the base layout
182                 // 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)
183                 // we need to check the custom location where the derived layouts will be
184                 foreach ( array ( MB_BASEMETADATALOCATION , MB_CUSTOMMETADATALOCATION ) as $type )
185                 {
186                         $sourceFilename = $this->getFileName ( $view, $moduleName, $type ) ;
187                         if($view == MB_POPUPSEARCH || $view == MB_POPUPLIST){
188                                 global $current_language;
189                                 $mod = return_module_language($current_language , $moduleName);
190                                 $layout = $this->_loadFromPopupFile ( $sourceFilename , $mod, $view);
191                         }else{
192                                 $layout = $this->_loadFromFile ( $sourceFilename );
193                         }
194                         if ( null !== ($layout ) )
195                         {
196                                 $this->_originalViewdefs = $layout ;
197                                 break ;
198                         }
199                 }
200                 //For quick create viewdefs, if there is no quickcreatedefs.php under MB_BASEMETADATALOCATION, the original defs is editview defs.
201         if ($view == MB_QUICKCREATE) {
202           foreach(array(MB_QUICKCREATE, MB_EDITVIEW) as $v){
203             $sourceFilename = $this->getFileName($v, $moduleName, MB_BASEMETADATALOCATION ) ;
204             if (file_exists($sourceFilename )) {
205               $layout = $this->_loadFromFile($sourceFilename );
206               if (null !== $layout && isset($layout[GridLayoutMetaDataParser::$variableMap[$v]])) {
207                 $layout = array(GridLayoutMetaDataParser::$variableMap[MB_QUICKCREATE] => $layout[GridLayoutMetaDataParser::$variableMap[$v]]);
208                 break;
209               }
210             }
211           }
212           
213           if (null === $layout) {
214             $sourceFilename = $this->getFileName($view, $moduleName, MB_CUSTOMMETADATALOCATION );
215             $layout = $this->_loadFromFile($sourceFilename );
216           }
217           
218           if (null !== $layout  ) {
219             $this->_originalViewdefs = $layout ;
220           }
221         }
222         
223                 $this->_fielddefs = $fielddefs ;
224                 $this->_history = new History ( $this->getFileName ( $view, $moduleName, MB_HISTORYMETADATALOCATION ) ) ;
225
226         }
227
228         function getLanguage ()
229         {
230                 return $this->_moduleName ;
231         }
232         
233         function getOriginalViewdefs()
234         {
235                 return $this->_originalViewdefs;
236         }
237
238
239         /*
240          * Save a draft layout
241          * @param array defs    Layout definition in the same format as received by the constructor
242          */
243         function save ($defs)
244         {
245                 //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.
246                 if ($this->_sourceFilename == $this->getFileName ( $this->_view, $this->_moduleName, MB_HISTORYMETADATALOCATION )) {
247                         foreach ( array ( MB_WORKINGMETADATALOCATION , MB_CUSTOMMETADATALOCATION , MB_BASEMETADATALOCATION ) as $type ) {
248                                 if (file_exists($this->getFileName ( $this->_view, $this->_moduleName, $type ))) {
249                                         $this->_history->append ( $this->getFileName ( $this->_view, $this->_moduleName, $type )) ;
250                                         break;
251                                 }
252                         }
253                 } else {
254                         $this->_history->append ( $this->_sourceFilename ) ;
255                 }
256
257                 $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->save(): writing to " . $this->getFileName ( $this->_view, $this->_moduleName, MB_WORKINGMETADATALOCATION ) ) ;
258                 $this->_saveToFile ( $this->getFileName ( $this->_view, $this->_moduleName, MB_WORKINGMETADATALOCATION ), $defs ) ;
259         }
260
261         /*
262          * Deploy a layout
263          * @param array defs    Layout definition in the same format as received by the constructor
264          */
265         function deploy ($defs)
266         {
267                 if ($this->_sourceFilename == $this->getFileName ( $this->_view, $this->_moduleName, MB_HISTORYMETADATALOCATION )) {
268                         foreach ( array ( MB_WORKINGMETADATALOCATION , MB_CUSTOMMETADATALOCATION , MB_BASEMETADATALOCATION ) as $type ) {
269                                 if (file_exists($this->getFileName ( $this->_view, $this->_moduleName, $type ))) {
270                                         $this->_history->append ( $this->getFileName ( $this->_view, $this->_moduleName, $type )) ;
271                                         break;
272                                 }
273                         }
274                 } else {
275                         $this->_history->append ( $this->_sourceFilename ) ;
276                 }
277                 // 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
278                 // 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
279                 $workingFilename = $this->getFileName ( $this->_view, $this->_moduleName, MB_WORKINGMETADATALOCATION ) ;
280
281                 if (file_exists ( $workingFilename ))
282                 unlink ( $this->getFileName ( $this->_view, $this->_moduleName, MB_WORKINGMETADATALOCATION ) ) ;
283                 $filename = $this->getFileName ( $this->_view, $this->_moduleName, MB_CUSTOMMETADATALOCATION ) ;
284                 $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->deploy(): writing to " . $filename ) ;
285                 $this->_saveToFile ( $filename, $defs ) ;
286
287                 // now clear the cache so that the results are immediately visible
288                 include_once ('include/TemplateHandler/TemplateHandler.php') ;
289                 TemplateHandler::clearCache ( $this->_moduleName ) ;
290         }
291
292         /*
293          * Construct a full pathname for the requested metadata
294          * Can be called statically
295          * @param string view           The view type, that is, EditView, DetailView etc
296          * @param string modulename     The name of the module that will use this layout
297          * @param string type
298          */
299         public static function getFileName ($view , $moduleName , $type = MB_CUSTOMMETADATALOCATION)
300         {
301
302                 $pathMap = array (
303             MB_BASEMETADATALOCATION => '' ,
304             MB_CUSTOMMETADATALOCATION => 'custom/' ,
305             MB_WORKINGMETADATALOCATION => 'custom/working/' ,
306             MB_HISTORYMETADATALOCATION => 'custom/history/'
307         ) ;
308                 $type = strtolower ( $type ) ;
309
310                 $filenames = array (
311             MB_DASHLETSEARCH => 'dashletviewdefs',
312             MB_DASHLET => 'dashletviewdefs',
313             MB_POPUPSEARCH => 'popupdefs',
314             MB_POPUPLIST => 'popupdefs',
315                         MB_LISTVIEW => 'listviewdefs' ,
316                         MB_BASICSEARCH => 'searchdefs' ,
317                         MB_ADVANCEDSEARCH => 'searchdefs' ,
318                         MB_EDITVIEW => 'editviewdefs' ,
319                         MB_DETAILVIEW => 'detailviewdefs' ,
320                         MB_QUICKCREATE => 'quickcreatedefs',
321                 ) ;
322
323         //In a deployed module, we can check for a studio module with file name overrides.
324         $sm = StudioModuleFactory::getStudioModule($moduleName);
325         foreach($sm->sources as $file => $def)
326         {
327             if (!empty($def['view'])) {
328                 $filenames[$def['view']] = substr($file, 0, strlen($file) - 4);
329             }
330
331         }
332
333                 // BEGIN ASSERTIONS
334                 if (! isset ( $pathMap [ $type ] ))
335                 {
336                         sugar_die ( "DeployedMetaDataImplementation->getFileName(): Type $type is not recognized" ) ;
337                 }
338                 if (! isset ( $filenames [ $view ] ))
339         {
340             sugar_die ( "DeployedMetaDataImplementation->getFileName(): View $view is not recognized" ) ;
341         }
342                 // END ASSERTIONS
343
344                 
345
346                 // Construct filename
347                 return $pathMap [ $type ] . 'modules/' . $moduleName . '/metadata/' . $filenames [ $view ] . '.php' ;
348         }
349         
350         private function replaceVariables($defs, $module) {
351                 $var_values = array(
352                         "<object_name>" => $module->seed->object_name, 
353                         "<_object_name>" => strtolower($module->seed->object_name),  
354                         "<OBJECT_NAME>" => strtoupper($module->seed->object_name), 
355                         "<module_name>" => $module->seed->module_dir,  
356                         '<_module_name>'=> strtolower ( $module->seed->module_dir ) 
357                 );
358                 return $this->recursiveVariableReplace($defs, $module, $var_values);
359         }
360
361         public function getModuleDir(){
362                 return $this->module_dir;
363         }
364     
365     private function recursiveVariableReplace($arr, $module, $replacements) {
366         $ret = array();
367                 foreach ($arr as $key => $val) {
368                         if (is_array($val)) {
369                     $newkey = $key;
370                 $val = $this->recursiveVariableReplace($val, $module, $replacements);
371                     foreach ($replacements as $var => $rep) {
372                         $newkey = str_replace($var, $rep, $newkey);
373                     }
374                                 $ret[$newkey] = $val;
375                 } else {
376                 $newkey = $key;
377                             $newval = $val;
378                 if(is_string($val))
379                 {
380                     foreach ($replacements as $var => $rep) {
381                         $newkey = str_replace($var, $rep, $newkey);
382                         $newval = str_replace($var, $rep, $newval);
383                     }
384                 }
385                 $ret[$newkey] = $newval;
386                         }
387         }
388                 return $ret;
389         }
390
391     /**
392      * This is just a wrapper to the private method _saveToFile
393      * @param  $file    the file name to save to
394      * @param  $defs    the defs to save to the file
395      * @return void
396      */
397     public function saveToFile($file, $defs)
398     {
399         $this->_saveToFile ( $file, $defs ) ;
400     }
401 }