]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - modules/ModuleBuilder/parsers/views/DeployedMetaDataImplementation.php
Release 6.2.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-2011 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 ( $this->_fileVariables [ $view ] ))
65                 {
66                         sugar_die ( get_class ( $this ) . ": View $view is not supported" ) ;
67                 }
68                 if (! isset ( $GLOBALS [ 'beanList' ] [ $moduleName ] ))
69                 {
70                         sugar_die ( get_class ( $this ) . ": Modulename $moduleName is not a Deployed Module" ) ;
71                 }
72                 // END ASSERTIONS
73
74                 $this->_view = strtolower ( $view ) ;
75                 $this->_moduleName = $moduleName ;
76
77                 $module = StudioModuleFactory::getStudioModule( $moduleName ) ;
78                 $this->module_dir = $module->seed->module_dir;
79                 $fielddefs = $module->getFields();
80
81                 $loaded = null ;
82                 foreach ( array ( MB_BASEMETADATALOCATION , MB_CUSTOMMETADATALOCATION , MB_WORKINGMETADATALOCATION , MB_HISTORYMETADATALOCATION ) as $type )
83                 {
84                         $this->_sourceFilename = $this->getFileName ( $view, $moduleName, $type ) ;
85                         if($view == MB_POPUPSEARCH || $view == MB_POPUPLIST){
86                                 global $current_language;
87                                 $mod = return_module_language($current_language , $moduleName);
88                                 $layout = $this->_loadFromPopupFile ( $this->_sourceFilename , $mod, $view);
89                         }else{
90                                 $layout = $this->_loadFromFile ( $this->_sourceFilename );
91                         }
92                         if ( null !== $layout )
93                         {
94                                 // merge in the fielddefs from this layout
95                                 $this->_mergeFielddefs ( $fielddefs , $layout ) ;
96                                 $loaded = $layout ;
97                         }
98                 }
99
100                 if ($loaded === null)
101                 {
102                         switch ( $view )
103                         {
104                                 case MB_QUICKCREATE:
105                                         // Special handling for QuickCreates - if we don't have a QuickCreate definition in the usual places, then use an EditView
106
107                                         $loaded = $this->_loadFromFile ( $this->getFileName ( MB_EDITVIEW, $this->_moduleName, MB_BASEMETADATALOCATION ) ) ;
108
109                                         if ($loaded === null)
110                                                 throw new Exception( get_class ( $this ) . ": cannot convert from EditView to QuickCreate for Module $this->_moduleName - definitions for EditView are missing" ) ;
111
112                                         // Now change the array index
113                                         $temp = $loaded [ GridLayoutMetaDataParser::$variableMap [ MB_EDITVIEW ] ] ;
114                                         unset ( $loaded [ GridLayoutMetaDataParser::$variableMap [ MB_EDITVIEW ] ] ) ;
115                                         $loaded [ GridLayoutMetaDataParser::$variableMap [ MB_QUICKCREATE ] ] = $temp ;
116                                         // finally, save out our new definition so that we have a base record for the history to work from
117                                         $this->_sourceFilename = self::getFileName ( MB_QUICKCREATE, $this->_moduleName, MB_CUSTOMMETADATALOCATION ) ;
118                                         $this->_saveToFile ( $this->_sourceFilename, $loaded ) ;
119                                         $this->_mergeFielddefs ( $fielddefs , $loaded ) ;
120                                         break;
121
122                                 case MB_DASHLETSEARCH:
123                         case MB_DASHLET:
124                                 $type = $module->getType () ;
125                                 $this->_sourceFilename = self::getFileName ( $view, $moduleName, MB_CUSTOMMETADATALOCATION ) ;
126                                 $needSave = false;
127                                 if(file_exists( "custom/modules/{$moduleName}/metadata/".basename ( $this->_sourceFilename))){
128                                         $loaded = $this->_loadFromFile ( "custom/modules/{$moduleName}/metadata/".basename ( $this->_sourceFilename) )  ;         
129                                 }
130                                 elseif(file_exists(
131                                         "modules/{$moduleName}/Dashlets/My{$moduleName}Dashlet/My{$moduleName}Dashlet.data.php")){
132                                         $loaded = $this->_loadFromFile ( "modules/{$moduleName}/Dashlets/My{$moduleName}Dashlet/My{$moduleName}Dashlet.data.php");
133                                 }
134                                 else{
135                                         $loaded = $this->_loadFromFile ( "include/SugarObjects/templates/$type/metadata/".basename ( $this->_sourceFilename ) ) ;
136                                         $needSave = true;                       
137                                 }
138                                 if ($loaded === null)
139                                                 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" ) ;
140                                 $loaded = $this->replaceVariables($loaded, $module);
141                                 $temp = $this->_moduleName;
142                                 if($needSave){
143                                         $this->_moduleName = $this->_moduleName.'Dashlet';
144                                                 $this->_saveToFile ( $this->_sourceFilename, $loaded,false) ; // write out without the placeholder module_name and object
145                                                 $this->_moduleName = $temp;
146                                                 unset($temp);
147                                 }
148                                         $this->_mergeFielddefs ( $fielddefs , $loaded ) ;
149                                         break;
150                                 case MB_POPUPLIST:
151                         case MB_POPUPSEARCH:
152                                 $type = $module->getType () ;
153                                         $this->_sourceFilename = self::getFileName ( $view, $moduleName, MB_CUSTOMMETADATALOCATION ) ;
154
155                                         // Now we can copy the wireless view from the template
156                                         global $current_language;
157                                         $mod = return_module_language($current_language , $moduleName);
158                                         $loadedForWrite = $this->_loadFromPopupFile (  "include/SugarObjects/templates/$type/metadata/".basename ( $this->_sourceFilename )  , $mod, $view, true);
159                                 if ($loadedForWrite === null)
160                                                 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" ) ;
161                                 $loadedForWrite = $this->replaceVariables($loadedForWrite, $module);
162                                         $this->_saveToFile ( $this->_sourceFilename, $loadedForWrite , false , true) ; // write out without the placeholder module_name and object
163                                         $loaded = $this->_loadFromPopupFile (  "include/SugarObjects/templates/$type/metadata/".basename ( $this->_sourceFilename )  , $mod, $view);
164                                         $this->_mergeFielddefs ( $fielddefs , $loaded ) ;
165                                 break;
166                                 default:
167
168                         }
169                         if ( $loaded === null )
170                                 throw new Exception( get_class ( $this ) . ": view definitions for View $this->_view and Module $this->_moduleName are missing" ) ;
171                 }
172
173                 $this->_viewdefs = $loaded ;
174                 // Set the original Viewdefs - required to ensure we don't lose fields from the base layout
175                 // 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)
176                 // we need to check the custom location where the derived layouts will be
177                 foreach ( array ( MB_BASEMETADATALOCATION , MB_CUSTOMMETADATALOCATION ) as $type )
178                 {
179                         $sourceFilename = $this->getFileName ( $view, $moduleName, $type ) ;
180                         if($view == MB_POPUPSEARCH || $view == MB_POPUPLIST){
181                                 global $current_language;
182                                 $mod = return_module_language($current_language , $moduleName);
183                                 $layout = $this->_loadFromPopupFile ( $sourceFilename , $mod, $view);
184                         }else{
185                                 $layout = $this->_loadFromFile ( $sourceFilename );
186                         }
187                         if ( null !== ($layout ) )
188                         {
189                                 $this->_originalViewdefs = $layout ;
190                                 break ;
191                         }
192                 }
193                 //For quick create viewdefs, if there is no quickcreatedefs.php under MB_BASEMETADATALOCATION, the original defs is editview defs.
194         if ($view == MB_QUICKCREATE) {
195           foreach(array(MB_QUICKCREATE, MB_EDITVIEW) as $v){
196             $sourceFilename = $this->getFileName($v, $moduleName, MB_BASEMETADATALOCATION ) ;
197             if (file_exists($sourceFilename )) {
198               $layout = $this->_loadFromFile($sourceFilename );
199               if (null !== $layout && isset($layout[GridLayoutMetaDataParser::$variableMap[$v]])) {
200                 $layout = array(GridLayoutMetaDataParser::$variableMap[MB_QUICKCREATE] => $layout[GridLayoutMetaDataParser::$variableMap[$v]]);
201                 break;
202               }
203             }
204           }
205           
206           if (null === $layout) {
207             $sourceFilename = $this->getFileName($view, $moduleName, MB_CUSTOMMETADATALOCATION );
208             $layout = $this->_loadFromFile($sourceFilename );
209           }
210           
211           if (null !== $layout  ) {
212             $this->_originalViewdefs = $layout ;
213           }
214         }
215         
216                 $this->_fielddefs = $fielddefs ;
217                 $this->_history = new History ( $this->getFileName ( $view, $moduleName, MB_HISTORYMETADATALOCATION ) ) ;
218
219         }
220
221         function getLanguage ()
222         {
223                 return $this->_moduleName ;
224         }
225         
226         function getOriginalViewdefs()
227         {
228                 return $this->_originalViewdefs;
229         }
230
231
232         /*
233          * Save a draft layout
234          * @param array defs    Layout definition in the same format as received by the constructor
235          */
236         function save ($defs)
237         {
238                 //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.
239                 if ($this->_sourceFilename == $this->getFileName ( $this->_view, $this->_moduleName, MB_HISTORYMETADATALOCATION )) {
240                         foreach ( array ( MB_WORKINGMETADATALOCATION , MB_CUSTOMMETADATALOCATION , MB_BASEMETADATALOCATION ) as $type ) {
241                                 if (file_exists($this->getFileName ( $this->_view, $this->_moduleName, $type ))) {
242                                         $this->_history->append ( $this->getFileName ( $this->_view, $this->_moduleName, $type )) ;
243                                         break;
244                                 }
245                         }
246                 } else {
247                         $this->_history->append ( $this->_sourceFilename ) ;
248                 }
249
250                 $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->save(): writing to " . $this->getFileName ( $this->_view, $this->_moduleName, MB_WORKINGMETADATALOCATION ) ) ;
251                 $this->_saveToFile ( $this->getFileName ( $this->_view, $this->_moduleName, MB_WORKINGMETADATALOCATION ), $defs ) ;
252         }
253
254         /*
255          * Deploy a layout
256          * @param array defs    Layout definition in the same format as received by the constructor
257          */
258         function deploy ($defs)
259         {
260                 if ($this->_sourceFilename == $this->getFileName ( $this->_view, $this->_moduleName, MB_HISTORYMETADATALOCATION )) {
261                         foreach ( array ( MB_WORKINGMETADATALOCATION , MB_CUSTOMMETADATALOCATION , MB_BASEMETADATALOCATION ) as $type ) {
262                                 if (file_exists($this->getFileName ( $this->_view, $this->_moduleName, $type ))) {
263                                         $this->_history->append ( $this->getFileName ( $this->_view, $this->_moduleName, $type )) ;
264                                         break;
265                                 }
266                         }
267                 } else {
268                         $this->_history->append ( $this->_sourceFilename ) ;
269                 }
270                 // 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
271                 // 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
272                 $workingFilename = $this->getFileName ( $this->_view, $this->_moduleName, MB_WORKINGMETADATALOCATION ) ;
273
274                 if (file_exists ( $workingFilename ))
275                 unlink ( $this->getFileName ( $this->_view, $this->_moduleName, MB_WORKINGMETADATALOCATION ) ) ;
276                 $filename = $this->getFileName ( $this->_view, $this->_moduleName, MB_CUSTOMMETADATALOCATION ) ;
277                 $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->deploy(): writing to " . $filename ) ;
278                 $this->_saveToFile ( $filename, $defs ) ;
279
280                 // now clear the cache so that the results are immediately visible
281                 include_once ('include/TemplateHandler/TemplateHandler.php') ;
282                 TemplateHandler::clearCache ( $this->_moduleName ) ;
283         }
284
285         /*
286          * Construct a full pathname for the requested metadata
287          * Can be called statically
288          * @param string view           The view type, that is, EditView, DetailView etc
289          * @param string modulename     The name of the module that will use this layout
290          * @param string type
291          */
292         public static function getFileName ($view , $moduleName , $type = MB_CUSTOMMETADATALOCATION)
293         {
294
295                 $pathMap = array ( MB_BASEMETADATALOCATION => '' , MB_CUSTOMMETADATALOCATION => 'custom/' , MB_WORKINGMETADATALOCATION => 'custom/working/' , MB_HISTORYMETADATALOCATION => 'custom/history/' ) ;
296                 $type = strtolower ( $type ) ;
297
298                 $filenames = array (
299             MB_DASHLETSEARCH => 'dashletviewdefs',
300             MB_DASHLET => 'dashletviewdefs',
301             MB_POPUPSEARCH => 'popupdefs',
302             MB_POPUPLIST => 'popupdefs',
303                         MB_LISTVIEW => 'listviewdefs' ,
304                         MB_BASICSEARCH => 'searchdefs' ,
305                         MB_ADVANCEDSEARCH => 'searchdefs' ,
306                         MB_EDITVIEW => 'editviewdefs' ,
307                         MB_DETAILVIEW => 'detailviewdefs' ,
308                         MB_QUICKCREATE => 'quickcreatedefs',
309                 ) ;
310
311                 // BEGIN ASSERTIONS
312                 if (! isset ( $pathMap [ $type ] ))
313                 {
314                         sugar_die ( "DeployedMetaDataImplementation->getFileName(): Type $type is not recognized" ) ;
315                 }
316                 if (! isset ( $filenames [ $view ] ))
317         {
318             sugar_die ( "DeployedMetaDataImplementation->getFileName(): View $view is not recognized" ) ;
319         }
320                 // END ASSERTIONS
321
322                 
323
324                 // Construct filename
325                 return $pathMap [ $type ] . 'modules/' . $moduleName . '/metadata/' . $filenames [ $view ] . '.php' ;
326         }
327         
328         private function replaceVariables($defs, $module) {
329                 $var_values = array(
330                         "<object_name>" => $module->seed->object_name, 
331                         "<_object_name>" => strtolower($module->seed->object_name),  
332                         "<OBJECT_NAME>" => strtoupper($module->seed->object_name), 
333                         "<module_name>" => $module->seed->module_dir,  
334                         '<_module_name>'=> strtolower ( $module->seed->module_dir ) 
335                 );
336                 return $this->recursiveVariableReplace($defs, $module, $var_values);
337         }
338
339         public function getModuleDir(){
340                 return $this->module_dir;
341         }
342     
343     private function recursiveVariableReplace($arr, $module, $replacements) {
344         $ret = array();
345                 foreach ($arr as $key => $val) {
346                         if (is_array($val)) {
347                     $newkey = $key;
348                 $val = $this->recursiveVariableReplace($val, $module, $replacements);
349                     foreach ($replacements as $var => $rep) {
350                         $newkey = str_replace($var, $rep, $newkey);
351                     }
352                                 $ret[$newkey] = $val;
353                 } else {
354                 $newkey = $key;
355                             $newval = $val;
356                                 foreach ($replacements as $var => $rep) {
357                     $newkey = str_replace($var, $rep, $newkey);
358                                         $newval = str_replace($var, $rep, $newval);
359                 }
360                 $ret[$newkey] = $newval;
361                         }
362         }
363                 return $ret;
364         }
365
366     /**
367      * This is just a wrapper to the private method _saveToFile
368      * @param  $file    the file name to save to
369      * @param  $defs    the defs to save to the file
370      * @return void
371      */
372     public function saveToFile($file, $defs)
373     {
374         $this->_saveToFile ( $file, $defs ) ;
375     }
376 }