]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - modules/ModuleBuilder/Module/StudioModule.php
Release 6.5.0
[Github/sugarcrm.git] / modules / ModuleBuilder / Module / StudioModule.php
1 <?php
2 /*********************************************************************************
3  * SugarCRM Community Edition is a customer relationship management program developed by
4  * SugarCRM, Inc. Copyright (C) 2004-2012 SugarCRM Inc.
5  * 
6  * This program is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU Affero General Public License version 3 as published by the
8  * Free Software Foundation with the addition of the following permission added
9  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
10  * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
11  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
12  * 
13  * This program is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
16  * details.
17  * 
18  * You should have received a copy of the GNU Affero General Public License along with
19  * this program; if not, see http://www.gnu.org/licenses or write to the Free
20  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21  * 02110-1301 USA.
22  * 
23  * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
24  * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
25  * 
26  * The interactive user interfaces in modified source and object code versions
27  * of this program must display Appropriate Legal Notices, as required under
28  * Section 5 of the GNU Affero General Public License version 3.
29  * 
30  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
31  * these Appropriate Legal Notices must retain the display of the "Powered by
32  * SugarCRM" logo. If the display of the logo is not reasonably feasible for
33  * technical reasons, the Appropriate Legal Notices must display the words
34  * "Powered by SugarCRM".
35  ********************************************************************************/
36
37
38 require_once 'data/BeanFactory.php';
39 require_once 'modules/ModuleBuilder/parsers/relationships/DeployedRelationships.php' ;
40 require_once 'modules/ModuleBuilder/parsers/constants.php' ;
41
42 class StudioModule
43 {
44     public $name ;
45     private $popups = array ( ) ;
46     public $module ;
47     public $fields ;
48     public $seed;
49
50     function __construct ($module)
51     {
52                 //Sources can be used to override the file name mapping for a specific view or the parser for a view.
53         //The
54         $this->sources = array (        'editviewdefs.php' => array ( 'name' => translate ('LBL_EDITVIEW') , 'type' => MB_EDITVIEW , 'image' => 'EditView' ) ,
55                                                                 'detailviewdefs.php' => array ( 'name' => translate('LBL_DETAILVIEW') , 'type' => MB_DETAILVIEW , 'image' => 'DetailView' ) ,
56                                                                 'listviewdefs.php' => array ( 'name' => translate('LBL_LISTVIEW') , 'type' => MB_LISTVIEW , 'image' => 'ListView' ) ) ;
57
58         $moduleNames = array_change_key_case ( $GLOBALS [ 'app_list_strings' ] [ 'moduleList' ] ) ;
59         $this->name = isset ( $moduleNames [ strtolower ( $module ) ] ) ? $moduleNames [ strtolower ( $module ) ] : strtolower ( $module ) ;
60         $this->module = $module ;
61         $this->seed = BeanFactory::getBean($this->module);
62         if($this->seed) {
63             $this->fields = $this->seed->field_defs;
64         }
65         //$GLOBALS['log']->debug ( get_class($this)."->__construct($module): ".print_r($this->fields,true) ) ;
66     }
67
68      /*
69      * Gets the name of this module. Some modules have naming inconsistencies such as Bug Tracker and Bugs which causes warnings in Relationships
70      * Added to resolve bug #20257
71      */
72     function getModuleName()
73     {
74         $modules_with_odd_names = array(
75         'Bug Tracker'=>'Bugs'
76         );
77         if ( isset ( $modules_with_odd_names [ $this->name ] ) )
78                 return ( $modules_with_odd_names [ $this->name ] ) ;
79
80         return $this->name;
81     }
82
83     /*
84      * Attempt to determine the type of a module, for example 'basic' or 'company'
85      * These types are defined by the SugarObject Templates in /include/SugarObjects/templates
86      * Custom modules extend one of these standard SugarObject types, so the type can be determined from their parent
87      * Standard module types can be determined simply from the module name - 'bugs' for example is of type 'issue'
88          * If all else fails, fall back on type 'basic'...
89          * @return string Module's type
90      */
91     function getType ()
92     {
93         // first, get a list of a possible parent types
94         $templates = array () ;
95         $d = dir ( 'include/SugarObjects/templates' ) ;
96                 while ( $filename = $d->read() )
97                 {
98                         if ( substr($filename,0,1) != '.' )
99                                 $templates [ strtolower ( $filename) ] = strtolower ( $filename ) ;
100                 }
101
102                 // If a custom module, then its type is determined by the parent SugarObject that it extends
103                 $type = $GLOBALS [ 'beanList' ] [ $this->module ] ;
104         require_once $GLOBALS [ 'beanFiles' ] [ $type ] ;
105
106         do
107         {
108                 $seed = new $type () ;
109                 $type = get_parent_class ($seed) ;
110         } while ( ! in_array ( strtolower ( $type ) , $templates ) && $type != 'SugarBean' ) ;
111
112         if ( $type != 'SugarBean' )
113         {
114                 return strtolower ( $type ) ;
115         }
116
117         // If a standard module then just look up its type - type is implicit for standard modules. Perhaps one day we will make it explicit, just as we have done for custom modules...
118                 $types = array (
119                 'Accounts' => 'company' , 
120                 'Bugs' => 'issue' , 
121                 'Cases' => 'issue' , 
122                 'Contacts' => 'person' , 
123                 'Documents' => 'file' , 
124                 'Leads' => 'person' , 
125                 'Opportunities' => 'sale'
126                 ) ;
127                 if ( isset ( $types [ $this->module ] ) )
128                         return $types [ $this->module ] ;
129
130         return "basic" ;
131     }
132
133     /*
134      * Return the fields for this module as sourced from the SugarBean
135      * @return  Array of fields
136      */
137
138     function getFields ()
139     {
140         return $this->fields ;
141     }
142
143     function getNodes ()
144     {
145         return array ( 'name' => $this->name , 'module' => $this->module , 'type' => 'StudioModule' , 'action' => "module=ModuleBuilder&action=wizard&view_module={$this->module}" , 'children' => $this->getModule() ) ;
146     }
147
148     function getModule ()
149     {
150         $sources = array (      translate('LBL_LABELS') => array ( 'action' => "module=ModuleBuilder&action=editLabels&view_module={$this->module}" , 'imageTitle' => 'Labels' , 'help' => 'labelsBtn' ) ,
151                                                 translate('LBL_FIELDS') => array ( 'action' => "module=ModuleBuilder&action=modulefields&view_package=studio&view_module={$this->module}" , 'imageTitle' => 'Fields' , 'help' => 'fieldsBtn'  ) ,
152                                                 translate('LBL_RELATIONSHIPS') => array ( 'action' => "get_tpl=true&module=ModuleBuilder&action=relationships&view_module={$this->module}" , 'imageTitle' => 'Relationships' , 'help' => 'relationshipsBtn' ) ,
153                                                 translate('LBL_LAYOUTS') => array ( 'children' => 'getLayouts' , 'action' => "module=ModuleBuilder&action=wizard&view=layouts&view_module={$this->module}" , 'imageTitle' => 'Layouts' , 'help' => 'layoutsBtn' ) ,
154                                                 translate('LBL_SUBPANELS') => array ( 'children' => 'getSubpanels' , 'action' => "module=ModuleBuilder&action=wizard&view=subpanels&view_module={$this->module}" , 'imageTitle' => 'Subpanels' , 'help' => 'subpanelsBtn' ) ) ;
155
156         $nodes = array () ;
157         foreach ( $sources as $source => $def )
158         {
159                 $nodes [ $source ] = $def ;
160                 $nodes [ $source ] [ 'name' ] = translate ( $source ) ;
161                 if ( isset ( $def [ 'children' ] ) )
162                 {
163                         $childNodes = $this->$def [ 'children' ] () ;
164                         if ( !empty ( $childNodes ) )
165                         {
166                                 $nodes [ $source ] [ 'type' ] = 'Folder' ;
167                                 $nodes [ $source ] [ 'children' ] = $childNodes ;
168                         }
169                         else
170                                 unset ( $nodes [ $source ] ) ;
171                 }
172         }
173
174         return $nodes ;
175     }
176     
177     function getViews() {
178         $views = array () ;
179         foreach ( $this->sources as $file => $def )
180         {
181             if (file_exists ( "modules/{$this->module}/metadata/$file" )
182                 || file_exists ( "custom/modules/{$this->module}/metadata/$file" ))
183             {
184                 $views [ str_replace ( '.php', '' , $file) ] = $def ;
185             }
186         }
187         return $views;
188     }
189
190     function getLayouts()
191     {
192         $views = $this->getViews();
193
194         // Now add in the QuickCreates - quickcreatedefs can be created by Studio from editviewdefs if they are absent, so just add them in regardless of whether the quickcreatedefs file exists
195
196         $hideQuickCreateForModules = array ( 'kbdocuments' , 'projecttask' , 
197             'campaigns'
198             ) ;
199         // Some modules should not have a QuickCreate form at all, so do not add them to the list
200         if (! in_array ( strtolower ( $this->module ), $hideQuickCreateForModules ))
201             $views [ 'quickcreatedefs' ] = array ( 'name' => translate('LBL_QUICKCREATE') , 'type' => MB_QUICKCREATE , 'image' => 'QuickCreate' ) ;
202
203         $layouts = array ( ) ;
204         foreach ( $views as $def )
205         {
206             $view = !empty($def['view']) ? $def['view'] : $def['type'];
207             $layouts [ $def['name'] ] = array ( 'name' => $def['name'] , 'action' => "module=ModuleBuilder&action=editLayout&view={$view}&view_module={$this->module}" , 'imageTitle' => $def['image'] , 'help' => "viewBtn{$def['type']}" , 'size' => '48' ) ;
208         }
209
210         if($this->isValidDashletModule($this->module)){
211                         $dashlets = array( );
212                 $dashlets [] = array('name' => translate('LBL_DASHLETLISTVIEW') , 'type' => 'dashlet' , 'action' => 'module=ModuleBuilder&action=editLayout&view=dashlet&view_module=' . $this->module );
213                         $dashlets [] = array('name' => translate('LBL_DASHLETSEARCHVIEW') , 'type' => 'dashletsearch' , 'action' => 'module=ModuleBuilder&action=editLayout&view=dashletsearch&view_module=' . $this->module );
214                         $layouts [ translate('LBL_DASHLET') ] = array ( 'name' => translate('LBL_DASHLET') , 'type' => 'Folder', 'children' => $dashlets,  'imageTitle' => 'Dashlet',  'action' => 'module=ModuleBuilder&action=wizard&view=dashlet&view_module=' . $this->module);             
215         }
216                 
217         //For popup tree node
218         $popups = array( );
219         $popups [] = array('name' => translate('LBL_POPUPLISTVIEW') , 'type' => 'popuplistview' , 'action' => 'module=ModuleBuilder&action=editLayout&view=popuplist&view_module=' . $this->module );
220                 $popups [] = array('name' => translate('LBL_POPUPSEARCH') , 'type' => 'popupsearch' , 'action' => 'module=ModuleBuilder&action=editLayout&view=popupsearch&view_module=' . $this->module );
221                 $layouts [ translate('LBL_POPUP') ] = array ( 'name' => translate('LBL_POPUP') , 'type' => 'Folder', 'children' => $popups, 'imageTitle' => 'Popup', 'action' => 'module=ModuleBuilder&action=wizard&view=popup&view_module=' . $this->module);  
222                         
223         $nodes = $this->getSearch () ;
224         if ( !empty ( $nodes ) )
225         {
226                 $layouts [ translate('LBL_SEARCH') ] = array ( 'name' => translate('LBL_SEARCH') , 'type' => 'Folder' , 'children' => $nodes , 'action' => "module=ModuleBuilder&action=wizard&view=search&view_module={$this->module}" , 'imageTitle' => 'BasicSearch' , 'help' => 'searchBtn' , 'size' => '48') ;
227         }
228
229         return $layouts ;
230
231     }
232
233         function isValidDashletModule($moduleName){
234                 $fileName = "My{$moduleName}Dashlet";
235                 $customFileName = "{$moduleName}Dashlet";
236                 if (file_exists ( "modules/{$moduleName}/Dashlets/{$fileName}/{$fileName}.php" )
237                         || file_exists ( "custom/modules/{$moduleName}/Dashlets/{$fileName}/{$fileName}.php" ) 
238                         || file_exists ( "modules/{$moduleName}/Dashlets/{$customFileName}/{$customFileName}.php" )
239                         || file_exists ( "custom/modules/{$moduleName}/Dashlets/{$customFileName}/{$customFileName}.php" ))
240         {
241                 return true;
242         }
243         return false;
244         }
245         
246
247     function getSearch ()
248     {
249                 require_once ('modules/ModuleBuilder/parsers/views/SearchViewMetaDataParser.php') ;
250
251                 $nodes = array () ;
252         foreach ( array ( MB_BASICSEARCH => 'LBL_BASIC_SEARCH' , MB_ADVANCEDSEARCH => 'LBL_ADVANCED_SEARCH' ) as $view => $label )
253         {
254                 try
255                 {
256                         $parser = new SearchViewMetaDataParser ( $view , $this->module ) ;
257                         $title = translate ( $label ) ;
258                         if($label == 'LBL_BASIC_SEARCH'){
259                                         $name = 'BasicSearch';
260                                 }elseif($label == 'LBL_ADVANCED_SEARCH'){
261                                         $name = 'AdvancedSearch';
262                                 }else{
263                                         $name = str_replace ( ' ', '', $title ) ;
264                                 }
265                 $nodes [ $title ] = array ( 'name' => $title , 'action' => "module=ModuleBuilder&action=editLayout&view={$view}&view_module={$this->module}" , 'imageTitle' => $title , 'imageName' => $name , 'help' => "{$name}Btn" , 'size' => '48' ) ;
266                 }
267                 catch ( Exception $e )
268                 {
269                         $GLOBALS [ 'log' ]->info( 'No search layout : '. $e->getMessage() ) ;
270                 }
271         }
272
273         return $nodes ;
274     }
275
276     /*
277      * Return an object containing all the relationships participated in by this module
278      * @return AbstractRelationships Set of relationships
279      */
280     function getRelationships ()
281     {
282         return new DeployedRelationships ( $this->module ) ;
283     }
284
285
286     /**
287      * Gets a list of subpanels used by the current module
288      */
289     function getSubpanels ()
290     {
291         if(!empty($GLOBALS['current_user']) && empty($GLOBALS['modListHeader']))
292             $GLOBALS['modListHeader'] = query_module_access_list($GLOBALS['current_user']);
293
294         require_once ('include/SubPanel/SubPanel.php') ;
295
296         $nodes = array ( ) ;
297
298             $GLOBALS [ 'log' ]->debug ( "StudioModule->getSubpanels(): getting subpanels for " . $this->module ) ;
299
300             // counter to add a unique key to assoc array below
301             $ct=0;
302             foreach ( SubPanel::getModuleSubpanels ( $this->module ) as $name => $label )
303             {
304                 if ($name == 'users')
305                     continue ;
306                 $subname = sugar_ucfirst ( (! empty ( $label )) ? translate ( $label, $this->module ) : $name ) ;
307                 $action = "module=ModuleBuilder&action=editLayout&view=ListView&view_module={$this->module}&subpanel={$name}&subpanelLabel=" . urlencode($subname);
308
309                 //  bug47452 - adding a unique number to the $nodes[ key ] so if you have 2+ panels
310                 //  with the same subname they will not cancel each other out
311                 $nodes [ $subname . $ct++ ] = array (
312                         'name' => $name , 
313                         'label' => $subname , 
314                         'action' =>  $action,
315                         'imageTitle' => $subname , 
316                         'imageName' => 'icon_' . ucfirst($name) . '_32', 
317                         'altImageName' => 'Subpanels', 
318                         'size' => '48' 
319                 ) ;
320             }
321
322         return $nodes ;
323
324     }
325
326     /**
327      * gets a list of subpanels provided to other modules
328      *
329      *
330      */
331     function getProvidedSubpanels ()
332     {
333         require_once 'modules/ModuleBuilder/parsers/relationships/AbstractRelationships.php' ;
334         $this->providedSubpanels = array () ;
335         $subpanelDir = 'modules/' . $this->module . '/metadata/subpanels/' ;
336         foreach(array($subpanelDir, "custom/$subpanelDir") as $dir)
337         {
338                 if (is_dir ( $dir ))
339                 {
340                     foreach(scandir($dir) as $fileName)
341                     {
342                         // sanity check to confirm that this is a usable subpanel...
343                         if (substr ( $fileName, 0, 1 ) != '.' && substr ( strtolower($fileName), -4 ) == ".php" 
344                                 && AbstractRelationships::validSubpanel ( "$dir/$fileName" ))
345                         {
346                             $subname = str_replace ( '.php', '', $fileName ) ;
347                             $this->providedSubpanels [ $subname ] = $subname ;
348                         }
349                     }
350                 }
351         }
352
353                 return $this->providedSubpanels;
354     }
355
356     
357     function getParentModulesOfSubpanel($subpanel){
358         global $moduleList, $beanFiles, $beanList, $module;
359     
360         //use tab controller function to get module list with named keys
361         require_once("modules/MySettings/TabController.php");
362         require_once("include/SubPanel/SubPanelDefinitions.php");
363         $modules_to_check = TabController::get_key_array($moduleList);
364
365         //change case to match subpanel processing later on
366         $modules_to_check = array_change_key_case($modules_to_check);
367     
368         $spd = '';
369         $spd_arr = array();
370         //iterate through modules and build subpanel array  
371         foreach($modules_to_check as $mod_name){
372             
373             //skip if module name is not in bean list, otherwise get the bean class name
374             if(!isset($beanList[$mod_name])) continue;
375             $class = $beanList[$mod_name];
376
377             //skip if class name is not in file list, otherwise require the bean file and create new class
378             if(!isset($beanFiles[$class]) || !file_exists($beanFiles[$class])) continue;
379             
380             //retrieve subpanels for this bean
381             require_once($beanFiles[$class]);
382             $bean_class = new $class();
383
384             //create new subpanel definition instance and get list of tabs
385             $spd = new SubPanelDefinitions($bean_class);
386             if (isset($spd->layout_defs['subpanel_setup'])) 
387             {
388                 foreach ($spd->layout_defs['subpanel_setup'] as $panelname) 
389                 {
390                     if ($panelname['module'] == $subpanel) 
391                     {
392                         $spd_arr[] = array('mod_name'   => $mod_name,
393                                            'panel_name' => $panelname['get_subpanel_data']);
394                     }
395                 }
396             }
397         }
398         return  $spd_arr;
399     }
400
401     function removeFieldFromLayouts ( $fieldName )
402     {
403         require_once("modules/ModuleBuilder/parsers/ParserFactory.php");
404         $GLOBALS [ 'log' ]->info ( get_class ( $this ) . "->removeFieldFromLayouts($fieldName)" ) ;
405         $sources = $this->getViewMetadataSources();
406         $sources[] = array('type'  => MB_BASICSEARCH);
407         $sources[] = array('type'  => MB_ADVANCEDSEARCH);
408         $sources[] = array('type'  => MB_POPUPSEARCH);        
409         
410         $GLOBALS [ 'log' ]->debug ( print_r( $sources,true) ) ;
411         foreach ( $sources as $name => $defs )
412         {
413             //If this module type doesn't support a given metadata type, we will get an exception from getParser()
414             try {
415                 $parser = ParserFactory::getParser( $defs [ 'type' ] , $this->module ) ;
416                 if ($parser->removeField ( $fieldName ) )
417                     $parser->handleSave(false) ; // don't populate from $_REQUEST, just save as is...
418             } catch(Exception $e){}
419         }
420         
421         //Remove the fields in subpanel
422         $data = $this->getParentModulesOfSubpanel($this->module);
423         foreach($data as $parentModule){
424             //If this module type doesn't support a given metadata type, we will get an exception from getParser()
425             try {
426                 $parser = ParserFactory::getParser(MB_LISTVIEW, $parentModule['mod_name'], null, $parentModule['panel_name']);
427                 if ($parser->removeField($fieldName)) 
428                 {
429                     $parser->handleSave(false);
430                 }
431             } catch(Exception $e){}
432         }
433     }
434
435         
436         
437         public function getViewMetadataSources() {
438                 $sources = $this->getViews();
439         $sources[] = array('type'  => MB_BASICSEARCH);
440         $sources[] = array('type'  => MB_ADVANCEDSEARCH);
441         $sources[] = array('type'  => MB_DASHLET);
442         $sources[] = array('type'  => MB_DASHLETSEARCH);
443         $sources[] = array('type'  => MB_POPUPLIST);
444         $sources[] = array('type'  => MB_QUICKCREATE);
445                 
446                 return $sources;
447         }
448
449     public function getViewType($view)
450     {
451         foreach($this->sources as $file => $def)
452         {
453             if (!empty($def['view']) && $def['view'] == $view && !empty($def['type']))
454             {
455                 return $def['type'];
456             }
457         }
458         return $view;
459     }
460         
461         
462 }
463 ?>