]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - modules/ModuleBuilder/parsers/relationships/AbstractRelationships.php
Release 6.4.1
[Github/sugarcrm.git] / modules / ModuleBuilder / parsers / relationships / AbstractRelationships.php
1 <?php
2 if (! defined ( 'sugarEntry' ) || ! sugarEntry)
3     die ( 'Not A Valid Entry Point' ) ;
4
5 /*********************************************************************************
6  * SugarCRM Community Edition is a customer relationship management program developed by
7  * SugarCRM, Inc. Copyright (C) 2004-2012 SugarCRM Inc.
8  * 
9  * This program is free software; you can redistribute it and/or modify it under
10  * the terms of the GNU Affero General Public License version 3 as published by the
11  * Free Software Foundation with the addition of the following permission added
12  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
13  * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
14  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
15  * 
16  * This program is distributed in the hope that it will be useful, but WITHOUT
17  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
19  * details.
20  * 
21  * You should have received a copy of the GNU Affero General Public License along with
22  * this program; if not, see http://www.gnu.org/licenses or write to the Free
23  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24  * 02110-1301 USA.
25  * 
26  * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
27  * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
28  * 
29  * The interactive user interfaces in modified source and object code versions
30  * of this program must display Appropriate Legal Notices, as required under
31  * Section 5 of the GNU Affero General Public License version 3.
32  * 
33  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
34  * these Appropriate Legal Notices must retain the display of the "Powered by
35  * SugarCRM" logo. If the display of the logo is not reasonably feasible for
36  * technical reasons, the Appropriate Legal Notices must display the words
37  * "Powered by SugarCRM".
38  ********************************************************************************/
39
40
41 /*
42  * Abstract class for managing a set of Relationships
43  * The Relationships we're managing consist of metadata about relationships, rather than relationship implementations used by the application
44  * Relationships defined here are implemented by the build() method to become a relationship that the application can use
45  * Note that the modules/Relationships/Relationship.php contains some methods that look similar; remember though that the methods in that file are acting on implemented relationships, not the metadata that we deal with here
46  */
47 class AbstractRelationships
48 {
49     
50     static $methods = array (
51         'Labels' => 'language' ,
52         'RelationshipMetaData' => 'relationships' ,
53         'SubpanelDefinitions' => 'layoutdefs' ,
54         'Vardefs' => 'vardefs' ,
55         'FieldsToLayouts' => 'layoutfields',
56     ) ;
57     static $activities = array ( 'calls' => 'Calls' , 'meetings' => 'Meetings' , 'notes' => 'Notes' , 'tasks' => 'Tasks' , 'emails' => 'Emails' ) ;
58     
59     protected $relationships = array ( ) ; // array containing all the AbstractRelationship objects that are in this set of relationships
60     protected $moduleName ;
61
62     // bug33522 - the following relationship names that you would find in $dictionary[ <relationshipName> ]
63     // have different actual relationship names other than <relationshipName>
64     // e.g $dictionary[ 'quotes_accounts' ] has two relationships: quotes_billto_accounts, quotes_shipto_accounts
65     protected $specialCaseBaseNames = array( 'quotes_accounts',
66                                              'quotes_contacts',
67                                              'emails_beans',
68                                              'linked_documents',
69                                              'project_relation',
70                                              'prospect_lists_prospects',
71                                              'queues_beans',
72                                              'queues_queue',
73                                              'tracker_sessions'
74                                           );
75     /*
76      * Find all deployed modules that can participate in a relationship
77      * Return a list of modules with associated subpanels
78      * @param boolean $includeActivitiesSubmodules True if the list should include Calls, Meetings etc; false if they should be replaced by the parent, Activities
79      * @return array    Array of [$module][$subpanel]
80      */
81     static function findRelatableModules ($includeActivitiesSubmodules = true)
82     {
83         $relatableModules = array ( ) ;
84         
85         // add in activities automatically if required
86         $relatableModules [ 'Activities' ] [ 'default' ] = translate( 'LBL_DEFAULT' ) ;
87             
88         // find all deployed modules
89         require_once 'modules/ModuleBuilder/Module/StudioBrowser.php' ;
90         $browser = new StudioBrowser() ;
91         $browser->loadRelatableModules();
92         reset($browser->modules) ;
93
94         while ( list( $moduleName , $module ) = each($browser->modules) )
95         {
96             // do not include the submodules of Activities as already have the parent...
97             if (! $includeActivitiesSubmodules && in_array ( $module->module, self::$activities ))
98                 continue ;
99             $providedSubpanels = $module->getProvidedSubpanels();
100             if ( $providedSubpanels !== false ) {
101                 $relatableModules [ $module->module ] = $providedSubpanels;
102             }
103         }
104         
105         return $relatableModules ;
106     
107     }
108
109     static function validSubpanel ($filename)
110     {
111         if (! file_exists ( $filename ))
112             return false ;
113         
114         include $filename ;
115         return (isset ( $subpanel_layout ) && (isset ( $subpanel_layout [ 'top_buttons' ] ) && isset ( $subpanel_layout [ 'list_fields' ] ))) ;
116     }
117
118     /*
119      * Get a list of all relationships (which have not been deleted)
120      * @return array    Array of relationship names, ready for use in get()
121      */
122     function getRelationshipList ()
123     {
124         $list = array ( ) ;
125         foreach ( $this->relationships as $name => $relationship )
126         {
127             if (! $relationship->deleted ())
128                 $list [ $name ] = $name ;
129         }
130         return $list ;
131     }
132
133     /*
134      * Get a relationship by name
135      * @param string $relationshipName  The unique name for this relationship, as returned by $relationship->getName()
136      * @return AbstractRelationship or false if $relationshipName is not in this set of relationships
137      */
138     function get ($relationshipName)
139     {
140         if (isset ( $this->relationships [ $relationshipName ] ))
141         {
142             return $this->relationships [ $relationshipName ] ;
143         }
144         return false ;
145     }
146
147     /*
148      * Construct a relationship from the information in the $_REQUEST array
149      * If a relationship_name is provided, and that relationship is not read only, then modify the existing relationship, overriding the definition with any AbstractRelationship::$definitionkeys entries set in the $_REQUEST
150      * Otherwise, create and add a new relationship with the information in the $_REQUEST
151      * @return AbstractRelationship
152      */
153     function addFromPost ()
154     {
155         $definition = array ( ) ;
156         
157         require_once 'modules/ModuleBuilder/parsers/relationships/AbstractRelationship.php' ;
158         foreach ( AbstractRelationship::$definitionKeys as $key )
159         {
160             if (! empty ( $_REQUEST [ $key ] ))
161             {
162                 $definition [ $key ] = ($key == 'relationship_type') ? AbstractRelationship::parseRelationshipType ( $_REQUEST [ $key ] ) : $_REQUEST [ $key ] ;
163             }
164         }
165         
166         // if this is a change to an existing relationship, and it is not readonly, then delete the old one
167         if (! empty ( $_REQUEST [ 'relationship_name' ] ))
168         {
169             if ($relationship = $this->get ( $_REQUEST [ 'relationship_name' ] ))
170             {
171                 unset( $definition[ 'relationship_name' ] ) ; // in case the related modules have changed; this name is probably no longer appropriate
172                 if (! $relationship->readonly ())
173                     $this->delete ( $_REQUEST [ 'relationship_name' ] ) ;
174         }
175         }
176         
177         $newRelationship = RelationshipFactory::newRelationship ( $definition ) ;
178         // TODO: error handling in case we get a badly formed definition and hence relationship
179         $this->add ( $newRelationship ) ;
180         return $newRelationship ;
181     }
182
183     /*
184      * Add a relationship to the set
185      * @param AbstractRelationship $relationship    The relationship to add
186      */
187     function add ($relationship)
188     {
189         $name = $this->getUniqueName ( $relationship ) ;
190         $relationship->setName ( $name ) ;
191         $this->relationships [ $name ] = $relationship ;
192     }
193
194     /*
195      * Load a set of relationships from a file
196      * The saved relationships are stored as AbstractRelationship objects, which isn't the same as the old MBRelationships definition
197      * @param string $basepath  Base directory in which to store the relationships information
198      * @return Array of AbstractRelationship objects
199      */
200     protected function _load ($basepath)
201     {
202         $GLOBALS [ 'log' ]->info ( get_class ( $this ) . ": loading relationships from " . $basepath . '/relationships.php' ) ;
203         $objects = array ( ) ;
204         if (file_exists ( $basepath . '/relationships.php' ))
205         {
206             include ($basepath . '/relationships.php') ;
207             foreach ( $relationships as $name => $definition )
208             {
209                 // update any pre-5.1 relationships to the new definitions
210                 // we do this here, rather than when upgrading from 5.0 to 5.1, as modules exported from MB in 5.0 may be loaded into 5.1 at any time
211                 // note also that since these definitions are only found in the relationships.php working file they only occur for deployed or exported modules, not published then loaded modules
212                 $definition = $this->_updateRelationshipDefinition( $definition ) ;
213                 $relationship = RelationshipFactory::newRelationship ( $definition ) ;
214                 // make sure it has a unique name
215                 if (! isset( $definition [ 'relationship_name' ] ) )
216                 {
217                     $name = $this->getUniqueName ( $relationship ) ;
218                     $relationship->setName ( $name ) ;
219                 }
220                 $objects [ $name ] = $relationship ;
221             }
222         }
223         return $objects ;
224     }
225
226     /*
227      * Save the set of relationships to a file
228      * @param string $basepath  Base directory in which to store the relationships information
229      */
230     protected function _save ($relationships , $basepath)
231     {
232         $GLOBALS [ 'log' ]->info ( get_class ( $this ) . ": saving relationships to " . $basepath . '/relationships.php' ) ;
233         $header = file_get_contents ( 'modules/ModuleBuilder/MB/header.php' ) ;
234         
235         $definitions = array ( ) ;
236         
237         foreach ( $relationships as $relationship )
238         {
239             // if (! $relationship->readonly ())
240             $definitions [ $relationship->getName () ] = $relationship->getDefinition () ;
241         }
242         
243         mkdir_recursive ( $basepath ) ;
244         // replace any existing relationships.php
245         write_array_to_file ( 'relationships', $definitions, $basepath . '/relationships.php', 'w', $header ) ;
246     }
247
248     /*
249      * Return all known deployed relationships
250      * All are set to read-only - the assumption for now is that we can't directly modify a deployed relationship
251      * However, if it was created through this AbstractRelationships class a modifiable version will be held in the relationships working file,
252      * and that one will override the readonly version in load()
253      *
254      * TODO: currently we ignore the value of the 'reverse' field in the relationships definition. This is safe to do as only one
255      * relationship (products-products) uses it (and there it makes no difference from our POV) and we don't use it when creating new ones
256      * @return array Array of $relationshipName => $relationshipDefinition as an array
257      */
258     protected function getDeployedRelationships ()
259     {
260         
261         $db = DBManagerFactory::getInstance () ;
262         $query = "SELECT * FROM relationships WHERE deleted = 0" ;
263         $result = $db->query ( $query ) ;
264         while ( $row = $db->fetchByAssoc ( $result ) )
265         {
266             // set this relationship to readonly
267             $row [ 'readonly' ] = true ;
268             $relationships [ $row [ 'relationship_name' ] ] = $row ;
269         }
270         
271         return $relationships ;
272     }
273
274     /*
275      * Get a name for this relationship that is unique across all of the relationships we are aware of
276      * We make the name unique by simply adding on a suffix until we achieve uniqueness
277      * @param AbstractRelationship The relationship object
278      * @return string A globally unique relationship name
279      */
280     protected function getUniqueName ($relationship)
281     {
282         $allRelationships = $this->getRelationshipList () ;
283         $basename = $relationship->getName () ;
284         
285         if (empty ( $basename ))
286         {
287             // start off with the proposed name being simply lhs_module_rhs_module
288             $definition = $relationship->getDefinition () ;
289             $basename = strtolower ( $definition [ 'lhs_module' ] . '_' . $definition [ 'rhs_module' ] ) ;
290         }
291         
292         $name = $basename ;
293         $suffix = 1 ;
294         while ( isset ( $allRelationships [ $name ] ) )
295         {
296             $name = $basename . "_" . ( string ) ($suffix ++) ;
297         }
298
299         // bug33522 - if our relationship basename is in the special cases
300         if( in_array( $name , $this->specialCaseBaseNames ) )  {
301             //add a _1 (or _suffix#) and check to see if it already exists
302             $name = $name . "_" . ( string ) ($suffix ++);
303             while ( isset ( $allRelationships [ $name ] ) )
304             {
305                 // if it does exist, strip off the _1 previously added and try again
306                 $name = substr( $name , 0 , -2 ) . "_" . ( string ) ($suffix ++);
307             }
308         }
309
310         return $name ;
311     }
312     
313     /*
314      * Translate the set of relationship objects into files that the Module Loader can work with
315      * @param string $basepath          Pathname of the directory to contain the build
316      * @param string $installDefPrefix  Pathname prefix for the installdefs, for example for ModuleBuilder use "<basepath>/SugarModules"
317      * @param array $relationships      Relationships to implement
318      */
319     protected function build ($basepath , $installDefPrefix , $relationships )
320     {
321         global $sugar_config;
322         // keep the relationships data separate from any other build data by ading /relationships to the basepath
323         $basepath .= '/relationships' ;
324
325         $installDefs = array ( ) ;
326         $compositeAdded = false ;
327         foreach ( self::$methods as $method => $key )
328         {
329             $buildMethod = 'build' . $method ;
330             $saveMethod = 'save' . $method ;
331             
332             foreach ( $relationships as $name => $relationship )
333             {
334                 if (! ($relationship->readonly () || $relationship->deleted ()))
335                 {
336                     if (method_exists ( $relationship, $buildMethod ) && method_exists ( $this, $saveMethod ))
337                     {
338                         $metadata = $relationship->$buildMethod () ;
339                         
340                         if (count ( $metadata ) > 0) // don't clutter up the filesystem with empty files...
341                         {
342                             $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . ": BUILD is running METHOD $saveMethod" ) ;
343                             $installDef = $this->$saveMethod ( $basepath, $installDefPrefix, $name, $metadata ) ;
344                             
345                             // some save methods (e.g., saveRelateFieldDefinition) handle the installDefs internally and so return null
346
347                         
348                             if (! is_null ( $installDef ))
349                             {
350                                 foreach ( $installDef as $moduleName => $def )
351                                 {
352                                     $installDefs [ $key ] [ ] = $def ;                                                                             
353                                 }
354                             }
355                         }
356                     }
357                 
358                 }
359             }
360         }
361         
362         return $installDefs ;
363     }
364
365     /*
366      * SAVE methods called during the build to translate the metadata provided by each relationship into files for the module installer
367      * Note that the installer expects only one file for each module in each section of the manifest - multiple files result in only the last one being implemented!
368      */
369     
370     /*
371      * Add a set of labels to the module
372      * @param string $basepath              Basepath location for this module
373      * @param $installDefPrefix             Pathname prefix for the installdefs, for example for ModuleBuilder use "<basepath>/SugarModules"
374      * @param string $relationshipName      Name of this relationship (for uniqueness)
375      * @param array $labelDefinitions       Array of System label => Display label pairs
376      * @return null Nothing to be added to the installdefs for an undeployed module
377      */
378     protected function saveLabels ($basepath , $installDefPrefix , $relationshipName , $labelDefinitions)
379     {
380         global $sugar_config;
381         
382         mkdir_recursive ( "$basepath/language" ) ;
383         
384         $headerString = "<?php\n//THIS FILE IS AUTO GENERATED, DO NOT MODIFY\n" ;
385         $installDefs = array ( ) ;
386         foreach ( $labelDefinitions as $definition )
387         {
388                 $mod_strings = array();
389                 $app_list_strings = array();
390                 
391                 $out = $headerString;
392                 
393                 $filename = "{$basepath}/language/{$definition['module']}.php" ;
394         
395                 if (file_exists ( $filename ))
396                         include ($filename);
397                         
398             
399             //Check for app strings
400             $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->saveLabels(): saving the following to {$filename}" 
401                                       . print_r ( $definition, true ) ) ;
402             if ($definition['module'] == 'application') {
403                 $app_list_strings[$definition [ 'system_label' ]] = $definition [ 'display_label' ];
404                 foreach ($app_list_strings as $key => $val)
405                         $out .= override_value_to_string_recursive2('app_list_strings', $key, $val);
406             } else {
407                 $mod_strings[ $definition [ 'system_label' ]] = $definition [ 'display_label' ];
408                 foreach ($mod_strings as $key => $val)
409                         $out .= override_value_to_string_recursive2('mod_strings', $key, $val);
410             }
411             
412             $fh = fopen ( $filename, 'w' ) ;
413             fputs ( $fh, $out, strlen ( $out ) ) ;
414             fclose ( $fh ) ;
415             
416                 
417             foreach($sugar_config['languages'] as $lk => $lv)
418             {
419                 $installDefs [ $definition [ 'module' ] . "_$lk" ] = array ( 
420                         'from' => "{$installDefPrefix}/relationships/language/{$definition [ 'module' ]}.php" , 
421                         'to_module' => $definition [ 'module' ] , 
422                         'language' => $lk 
423                 ) ;                                             
424             }
425             
426             /* do not use the following write_array_to_file method to write the label file - 
427              * module installer appends each of the label files together (as it does for all files) 
428                          * into a combined label file and so the last $mod_strings is the only one received by the application */
429                 // write_array_to_file ( 'mod_strings', array ( $definition [ 'system_label' ] => $definition [ 'display_label' ] ), $filename, "a" ) ;
430         }
431         
432         return $installDefs ;
433     }
434
435     /*
436      * Translate a set of relationship metadata definitions into files for the Module Loader
437      * @param string $basepath              Basepath location for this module
438      * @param $installDefPrefix             Pathname prefix for the installdefs, for example for ModuleBuilder use "<basepath>/SugarModules"
439      * @param string $relationshipName      Name of this relationship (for uniqueness)
440      * @param array $relationshipMetaData   Set of metadata definitions in the form $relationshipMetaData[$relationshipName]
441      * @return array $installDefs           Set of new installDefs
442      */
443     protected function saveRelationshipMetaData ($basepath , $installDefPrefix , $relationshipName , $relationshipMetaData)
444     {
445         mkdir_recursive ( "$basepath/relationships" ) ;
446         
447         $installDefs = array ( ) ;
448         list ( $rhs_module, $properties ) = each ( $relationshipMetaData ) ;
449         $filename = "$basepath/relationships/{$relationshipName}MetaData.php" ;
450         $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->saveRelationshipMetaData(): saving the following to {$filename}" . print_r ( $properties, true ) ) ;
451         write_array_to_file ( 'dictionary["' . $relationshipName . '"]', $properties, "{$filename}", 'w' ) ;
452         $installDefs [ $relationshipName ] = array ( /*'module' => $rhs_module , 'module_vardefs' => "<basepath>/Vardefs/{$relationshipName}.php" ,*/ 'meta_data' => "{$installDefPrefix}/relationships/relationships/{$relationshipName}MetaData.php" ) ;
453         
454         return $installDefs ;
455     }
456
457     /*
458      * Translate a set of subpanelDefinitions into files for the Module Loader
459      * @param string $basepath              Basepath location for this module
460      * @param $installDefPrefix             Pathname prefix for the installdefs, for example for ModuleBuilder use "<basepath>/SugarModules"
461      * @param array $subpanelDefinitions    Set of subpanel definitions in the form $subpanelDefinitions[$for_module][]
462      * @return array $installDefs           Set of new installDefs
463      */
464     protected function saveSubpanelDefinitions ($basepath , $installDefPrefix , $relationshipName , $subpanelDefinitions)
465     {
466         mkdir_recursive ( "$basepath/layoutdefs/" ) ;
467         
468         foreach ( $subpanelDefinitions as $moduleName => $definitions )
469         {
470             $filename = "$basepath/layoutdefs/{$relationshipName}_{$moduleName}.php" ;
471             $subpanelVarname = 'layout_defs["' . $moduleName . '"]["subpanel_setup"]';
472             $out = "";
473             foreach ( $definitions as $definition )
474             {
475                 $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->saveSubpanelDefinitions(): saving the following to {$filename}" . print_r ( $definition, true ) ) ;
476                 if (empty($definition ['get_subpanel_data']) || $definition ['subpanel_name'] == 'history' || $definition ['subpanel_name'] == 'activities') {
477                     $definition ['get_subpanel_data'] = $definition ['subpanel_name'];
478                 }
479                 $out .= override_value_to_string($subpanelVarname, strtolower ( $definition [ 'get_subpanel_data' ] ), $definition) . "\n";
480             }
481             if (!empty($out)) {
482                 $out = "<?php\n // created: " . date('Y-m-d H:i:s') . "\n" . $out;
483                 sugar_file_put_contents($filename, $out);
484             }
485
486             $installDefs [ $moduleName ] = array ( 'from' => "{$installDefPrefix}/relationships/layoutdefs/{$relationshipName}_{$moduleName}.php" , 'to_module' => $moduleName ) ;
487         }
488         return $installDefs ;
489     }
490
491
492     /*
493      * Translate a set of linkFieldDefinitions into files for the Module Loader
494      * Note that the Module Loader will only accept one entry in the vardef section of the Manifest for each module
495      * This means that we cannot simply build a file for each relationship as relationships that involve the same module will end up overwriting each other when installed
496      * So we have to append the vardefs for each relationship to a single file for each module
497      * @param string $basepath              Basepath location for this module
498      * @param $installDefPrefix             Pathname prefix for the installdefs, for example for ModuleBuilder use "<basepath>/SugarModules"
499      * @param string $relationshipName      Name of this relationship (for uniqueness)
500      * @param array $linkFieldDefinitions   Set of link field definitions in the form $linkFieldDefinitions[$for_module]
501      * @return array $installDefs           Set of new installDefs
502      */
503     protected function saveVardefs ($basepath , $installDefPrefix , $relationshipName , $vardefs)
504     {
505         mkdir_recursive ( "$basepath/vardefs/" ) ;
506         $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->saveVardefs(): vardefs =" . print_r ( $vardefs, true ) ) ;
507         
508         foreach ( $vardefs as $moduleName => $definitions )
509         {
510             // find this module's Object name - the object name, not the module name, is used as the key in the vardefs...
511             if (isset ( $GLOBALS [ 'beanList' ] [ $moduleName ] ))
512             {
513                 $module = get_module_info ( $moduleName ) ;
514                 $object = $module->object_name ;
515             } else
516             {
517                 $object = $moduleName ;
518             }
519             
520             $relName = $moduleName;
521             foreach ( $definitions as $definition )
522             {
523                 if (!empty($definition['relationship']))
524                 {
525                         $relName = $definition['relationship'];
526                         break;
527                 }
528             }
529             
530             $filename = "$basepath/vardefs/{$relName}_{$moduleName}.php" ;
531             
532             $out =  "<?php\n// created: " . date('Y-m-d H:i:s') . "\n";
533             foreach ( $definitions as $definition )
534             {
535                 $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->saveVardefs(): saving the following to {$filename}" . print_r ( $definition, true ) ) ;
536                 $out .= '$dictionary["' . $object . '"]["fields"]["' . $definition [ 'name' ] . '"] = '
537                           . var_export_helper($definition) . ";\n";
538             }
539             file_put_contents($filename, $out);
540             
541             $installDefs [ $moduleName ] = array ( 
542                 'from' => "{$installDefPrefix}/relationships/vardefs/{$relName}_{$moduleName}.php" , 
543                 'to_module' => $moduleName 
544             ) ;
545         }
546         
547         $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->saveVardefs(): installDefs =" . print_r ( $installDefs, true ) ) ;
548         
549         return $installDefs ;
550     
551     }
552
553     /*
554      * Determine if we're dealing with a deployed or undeployed module based on the name
555      * Undeployed modules are those known to ModuleBuilder; the twist is that the deployed names of modulebuilder modules are keyname_modulename not packagename_modulename
556      * and ModuleBuilder doesn't have any accessor methods based around keys, so we must convert keynames to packagenames
557      * @param $deployedName Name of the module in the deployed form - that is, keyname_modulename or modulename
558      * @return array ('moduleName'=>name, 'packageName'=>package) if undeployed, ('moduleName'=>name) if deployed
559      */
560     static function parseDeployedModuleName ($deployedName)
561     {
562         require_once 'modules/ModuleBuilder/MB/ModuleBuilder.php' ;
563         $mb = new ModuleBuilder ( ) ;
564         
565         $packageName = '' ;
566         $moduleName = $deployedName ;
567         
568         foreach ( $mb->getPackageList () as $name )
569         {
570             // convert the keyName into a packageName, needed for checking to see if this is really an undeployed module, or just a module with a _ in the name...
571             $package = $mb->getPackage ( $name ) ; // seem to need to call getPackage twice to get the key correctly... TODO: figure out why...
572             $key = $mb->getPackage ( $name )->key ;
573             if (strlen ( $key ) < strlen ( $deployedName ))
574             {
575                 $position = stripos ( $deployedName, $key ) ;
576                 $moduleName = trim( substr( $deployedName , strlen($key) ) , '_' ); //use trim rather than just assuming that _ is between packageName and moduleName in the deployedName
577                 if ( $position !== false && $position == 0 && (isset ( $mb->packages [ $name ]->modules [ $moduleName ] )))
578                 {
579                     $packageName = $name ;
580                     break ;
581                 }
582             }
583         }
584         
585         if (! empty ( $packageName ))
586         {
587             return array ( 'moduleName' => $moduleName , 'packageName' => $packageName ) ;
588         } else
589         {
590             return array ( 'moduleName' => $deployedName ) ;
591         }
592     }
593
594
595 }