]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - modules/ModuleBuilder/parsers/relationships/UndeployedRelationships.php
Release 6.5.0
[Github/sugarcrm.git] / modules / ModuleBuilder / parsers / relationships / UndeployedRelationships.php
1 <?php
2 if (! defined ( 'sugarEntry' ) || ! sugarEntry)
3     die ( 'Not A Valid Entry Point' ) ;
4 /*********************************************************************************
5  * SugarCRM Community Edition is a customer relationship management program developed by
6  * SugarCRM, Inc. Copyright (C) 2004-2012 SugarCRM Inc.
7  * 
8  * This program is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU Affero General Public License version 3 as published by the
10  * Free Software Foundation with the addition of the following permission added
11  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
12  * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
13  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
14  * 
15  * This program is distributed in the hope that it will be useful, but WITHOUT
16  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
18  * details.
19  * 
20  * You should have received a copy of the GNU Affero General Public License along with
21  * this program; if not, see http://www.gnu.org/licenses or write to the Free
22  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23  * 02110-1301 USA.
24  * 
25  * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
26  * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
27  * 
28  * The interactive user interfaces in modified source and object code versions
29  * of this program must display Appropriate Legal Notices, as required under
30  * Section 5 of the GNU Affero General Public License version 3.
31  * 
32  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
33  * these Appropriate Legal Notices must retain the display of the "Powered by
34  * SugarCRM" logo. If the display of the logo is not reasonably feasible for
35  * technical reasons, the Appropriate Legal Notices must display the words
36  * "Powered by SugarCRM".
37  ********************************************************************************/
38
39
40 require_once 'modules/ModuleBuilder/parsers/relationships/AbstractRelationships.php' ;
41 require_once 'modules/ModuleBuilder/parsers/relationships/RelationshipsInterface.php' ;
42 require_once 'modules/ModuleBuilder/parsers/relationships/RelationshipFactory.php' ;
43
44 class UndeployedRelationships extends AbstractRelationships implements RelationshipsInterface
45 {
46     
47     protected $basepath ; // Base directory for the lhs_module
48     protected $packageName ;
49     private $activitiesToAdd ; // if we need to add in the composite Activities and History subpanels to the module during the build
50
51     
52     /*
53      * Constructor
54      * Automatically loads in any saved relationships
55      * @param string $path  The pathname of the base module directory
56      */
57     function __construct ($path)
58     {
59         $this->basepath = $path ;
60         // pull the module and package names out of the path
61         $this->moduleName = basename ( $path, "/" ) ; // just in case there are any trailing /
62         $this->packageName = basename ( dirname ( dirname ( $path ) ) ) ; // simpler than explode :)
63         require_once 'modules/ModuleBuilder/MB/ModuleBuilder.php' ;
64         $mb = new ModuleBuilder ( ) ;
65         $this->packageKey = $mb->getPackageKey ( $this->packageName ) ;
66         
67         $this->load () ;
68     
69     }
70
71     /*
72      * Find all modules, deployed and undeployed, that can participate in a relationship
73      * @return array    Array of [$module][$subpanel]
74      */
75     static function findRelatableModules ()
76     {
77         // first find all deployed modules that we might participate in a relationship
78         $relatableModules = parent::findRelatableModules ( ) ;
79         
80         // now add in the undeployed modules - those in custom/modulebuilder
81         // note that if a module exists in both deployed and undeployed forms, the subpanels from the undeployed form are used...  
82
83         require_once 'modules/ModuleBuilder/MB/ModuleBuilder.php' ;
84         $mb = new ModuleBuilder ( ) ;
85         $mb->getPackages () ;
86         foreach ( $mb->getPackageList () as $packageName )
87         {
88             $package = $mb->packages [ $packageName ] ;
89             foreach ( $package->modules as $module )
90             {
91                 $relatableModules [ $package->key . "_" . $module->name ] = $module->getProvidedSubpanels () ;
92             }
93         }
94         
95         return $relatableModules ;
96     
97     }
98
99     /*
100      * Add a relationship to the set
101      * For undeployed relationships we always make the fields in the relationship visible in the layouts now, rather than waiting until build time, so
102      * that the admin may move them around or otherwise edit them before the module is deployed
103      * @param AbstractRelationship $relationship    The relationship to add
104      */
105     function add ($relationship)
106     {
107         parent::add ( $relationship ) ;
108         $this->addFieldsToUndeployedLayouts ( $relationship ) ; // must come after parent::add as we need the relationship_name in the relationships getFieldsToLayouts() which is called by addFieldsToUndeployedLayouts() 
109     }
110
111     /*
112      * Delete a relationship by name
113      * In future, if we need to actually track deleted relationships then just call $relationship->delete() instead
114      * @param string $relationshipName  The unique name for this relationship, as returned by $relationship->getName()
115      */
116     function delete ($relationshipName)
117     {
118         if ($relationship = $this->get ( $relationshipName ))
119         {
120             $this->removeFieldsFromUndeployedLayouts ( $relationship ) ;
121             unset ( $this->relationships [ $relationshipName ] ) ;
122         }
123     }
124
125     /*
126      * Load the saved relationship definitions for this module
127      */
128     function load ()
129     {
130         $this->relationships = parent::_load ( $this->basepath ) ;
131     }
132
133     /*
134      * Save this modules relationship definitions out to a working file
135      */
136     function save ()
137     {
138         parent::_save ( $this->relationships, $this->basepath ) ;
139     }
140
141     /*
142      * Update pre-5.1 relationships to the 5.1 relationship definition
143      * @param array definition  The 5.0 relationship definition
144      * @return array            The definition updated to 5.1 format
145      */
146     protected function _updateRelationshipDefinition ($definition)
147     {
148         if (isset ( $definition [ 'relate' ] ))
149         {
150             $newDefinition = array ( ) ;
151             foreach ( array ( 'relate' => 'rhs_module' , 'rsub' => 'rhs_subpanel' , 'msub' => 'lhs_subpanel' , 'label' => 'label' ) as $oldParameter => $newParameter )
152             {
153                 if (isset ( $definition [ $oldParameter ] ))
154                 {
155                     $definition [ $newParameter ] = $definition [ $oldParameter ] ;
156                     unset ( $definition [ $oldParameter ] ) ;
157                 }
158             }
159             $definition [ 'lhs_module' ] = "{$this->packageKey}_{$this->moduleName}" ;
160             // finally update the relationship name
161             unset ( $definition [ 'name' ] ) ; // clear the oldstyle name
162         }
163         return $definition ;
164     }
165
166     /*
167      * Implementation of getAllRelationships() for Undeployed modules
168      * The set of all relevant relationships for undeployed modules is the superset of that for deployed modules and all of the relationships known to ModuleBuilder
169      * @return array Set of all relevant relationships
170      */
171     protected function getAllRelationships ()
172     {
173         // start with the set of relationships known to this module plus those already deployed
174         $allRelationships = array_merge ( $this->relationships, parent::getDeployedRelationships () ) ;
175         
176         // add in the relationships known to ModuleBuilder
177         require_once 'modules/ModuleBuilder/MB/ModuleBuilder.php' ;
178         $mb = new ModuleBuilder ( ) ;
179         $mb->getPackages () ;
180         foreach ( $mb->getPackageList () as $packageName )
181         {
182             $package = $mb->packages [ $packageName ] ;
183             foreach ( $package->modules as $module )
184             {
185                 
186                 foreach ( $module->relationships->getRelationshipList () as $relationshipName )
187                 {
188                     $relationship = $module->relationships->get ( $relationshipName ) ;
189                     $allRelationships [ $relationship->getName () ] = $relationship->getDefinition () ;
190                 }
191             }
192         }
193         
194         return $allRelationships ;
195     
196     }
197
198     /*
199      * As of SugarCRM 5.1 the subpanel code and the widgets have difficulty handling multiple subpanels or relate fields from the same module
200      * Until this is fixed, we new relationships which will trigger this problem must be flagged as "relationship_only" and built without a UI component
201      * This function is called from the view when constructing a new relationship
202      * @param AbstractRelationship $relationship The relationship to be enforced
203      */
204     public function enforceRelationshipOnly ($relationship)
205     {
206         // if we already have a relationship between this lhs_module and this rhs_module then set RelationshipOnly flag
207         foreach ( $this->relationships as $rel )
208         {
209             if ($rel->lhs_module == $relationship->lhs_module && $rel->rhs_module == $relationship->rhs_module)
210             {
211                 $rel->setRelationship_only () ;
212                 break ;
213             }
214         }
215     }
216
217     /*
218      * BUILD FUNCTIONS
219      */
220     
221     /*
222      * Translate the set of relationship objects into files that the Module Loader can work with
223      * @param $basepath string Pathname of the directory to contain the build
224      */
225     function build ($basepath)
226     {
227         
228         // first expand out any reference to Activities to its submodules
229         // we do this here rather than in the subcomponents of the build as most of those subcomponents make use of elements of the definition, such
230         // as the relationship name, that must be unique
231         // the only special case is the subpanel for Activities, which is a composite, and is applied only once for all the submodules - this is handled in saveSubpanelDefinitions() for Undeployed modules
232         
233         $relationships = array ( ) ;
234         $this->activitiesToAdd = false ;
235         foreach ( $this->relationships as $relationshipName => $relationship )
236         {
237             $definition = $relationship->getDefinition () ;
238             // activities will always appear on the rhs only - lhs will be always be this module in MB
239             if (strtolower ( $definition [ 'rhs_module' ] ) == 'activities')
240             {
241                 $this->activitiesToAdd = true ;
242                 $relationshipName = $definition [ 'relationship_name' ] ;
243                 foreach ( self::$activities as $activitiesSubModuleLower => $activitiesSubModuleName )
244                 {
245                     $definition [ 'rhs_module' ] = $activitiesSubModuleName ;
246                     $definition [ 'for_activities' ] = true ;
247                     $definition [ 'relationship_name' ] = $relationshipName . '_' . $activitiesSubModuleLower ;
248                     $relationships [ $definition [ 'relationship_name' ] ] = RelationshipFactory::newRelationship ( $definition ) ;
249                 }
250             
251             } else
252             {
253                 $relationships [ $definition [ 'relationship_name' ] ] = $relationship ;
254             }
255         }
256         
257         require_once 'modules/ModuleBuilder/MB/ModuleBuilder.php' ;
258         $mb = new ModuleBuilder ( ) ;
259         $module = $mb->getPackageModule ( $this->packageName, $this->moduleName ) ;
260         if ($this->activitiesToAdd)
261         {
262             $appStrings = $module->getAppListStrings () ;
263             $appStrings [ 'parent_type_display' ] [ $module->key_name ] = $module->getlabel ( 'en_us', 'LBL_MODULE_TITLE' ) ;
264             $appStrings [ 'record_type_display' ] [ $module->key_name ] = $module->getlabel ( 'en_us', 'LBL_MODULE_TITLE' ) ;
265             $appStrings [ 'record_type_display_notes' ] [ $module->key_name ] = $module->getlabel ( 'en_us', 'LBL_MODULE_TITLE' ) ;
266             $module->setAppListStrings ( 'en_us', $appStrings ) ;
267             $module->save () ;
268
269         }
270                 else
271                 {
272                         //Bug42170================================
273                         $appStrings = $module->getAppListStrings () ;
274             unset($appStrings [ 'parent_type_display' ] [ $module->key_name ]); 
275                         unset($appStrings [ 'record_type_display' ] [ $module->key_name ]);
276                         unset($appStrings [ 'record_type_display_notes' ] [ $module->key_name ]);
277                     $module->setAppListStrings ( 'en_us', $appStrings ) ;
278             $module->save () ;
279                         //Bug42170================================
280                 }
281         
282         // use an installDefPrefix of <basepath>/SugarModules for compatibility with the rest of ModuleBuilder
283         $this->installDefs = parent::build ( $basepath, "<basepath>/SugarModules", $relationships ) ;
284     }
285
286     /*
287      * Add the installDefs for this relationship to the definitions in the parameter
288      * Required by MBModule
289      * @param reference installDef  Reference to the set of installDefs to which this relationship's installDefs should be added
290      */
291     function addInstallDefs (&$installDef)
292     {
293         foreach ( $this->installDefs as $name => $def )
294         {
295             if (! empty ( $def ))
296             {
297                 foreach ( $def as $val )
298                 {
299                     $installDef [ $name ] [] = $val ;
300                 }
301             }
302         }
303     }
304
305     private function addFieldsToUndeployedLayouts ($relationship)
306     {
307         return $this->updateUndeployedLayout ( $relationship, true ) ;
308     }
309
310     private function removeFieldsFromUndeployedLayouts ($relationship)
311     {
312         return $this->updateUndeployedLayout ( $relationship, false ) ;
313     }
314
315     /**
316      * @param AbstractRelationship $relationship
317      * @return void
318      */
319     private function removeAppLangStrings($relationship) {
320         $def = $relationship->getDefinition();
321         if (strtolower ( $def [ 'rhs_module' ] ) == 'activities' && !empty($_REQUEST [ 'view_package' ]) && !empty($_REQUEST [ 'view_module' ] ))
322         {
323             $mb = new ModuleBuilder ( ) ;
324             $module = $mb->getPackageModule ( $_REQUEST [ 'view_package' ], $_REQUEST [ 'view_module' ] ) ;
325             $appStrings = $module->getAppListStrings () ;
326             foreach(array('parent_type_display', 'record_type_display', 'record_type_display_notes') as $key)
327             {
328                 if (isset($appStrings[$key][ $module->key_name ]))
329                     unset($appStrings[$key][ $module->key_name ]);
330             }
331             $module->setAppListStrings ( 'en_us', $appStrings ) ;
332             $module->save () ;
333         }
334     }
335
336     /*
337      * Add any relate fields to the DetailView and EditView of the appropriate module immediately (don't wait for a build)
338      * @param AbstractRelationship $relationship The relationship whose fields we are to add or remove
339      * @param boolean $actionAdd True if we are to add; false if to remove
340      * return null
341      */
342     private function updateUndeployedLayout ($relationship , $actionAdd = true)
343     {
344         
345         // many-to-many relationships don't have fields so if we have a many-to-many we can just skip this...
346         if ($relationship->getType () == MB_MANYTOMANY)
347             return false ;
348         
349         $successful = true ;
350         $layoutAdditions = $relationship->buildFieldsToLayouts () ;
351         
352         require_once 'modules/ModuleBuilder/parsers/views/GridLayoutMetaDataParser.php' ;
353         foreach ( $layoutAdditions as $deployedModuleName => $fieldName )
354         {
355             foreach ( array ( MB_EDITVIEW , MB_DETAILVIEW ) as $view )
356             {
357                 $parsedName = AbstractRelationships::parseDeployedModuleName ( $deployedModuleName ) ;
358                 if (isset ( $parsedName [ 'packageName' ] ))
359                 {
360                     $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . ": " . (($actionAdd) ? "adding" : "removing") . " $fieldName on $view layout for undeployed module {$parsedName [ 'moduleName' ]} in package {$parsedName [ 'packageName' ]}" ) ;
361                     $parser = new GridLayoutMetaDataParser ( $view, $parsedName [ 'moduleName' ], $parsedName [ 'packageName' ] ) ;
362                     
363                     if (($actionAdd) ? $parser->addField ( array ( 'name' => $fieldName ) ) : $parser->removeField ( $fieldName ))
364                     {
365                         $parser->handleSave ( false ) ;
366                     } 
367                     else
368                     {
369                         $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . ": couldn't " . (($actionAdd) ? "add" : "remove") . " $fieldName on $view layout for undeployed module $deployedModuleName" ) ;
370                         $successful = false ;
371                     }
372                 }
373             }
374         }
375         
376         return $successful ;
377     }
378
379     /*
380      * Add any fields to the DetailView and EditView of the appropriate modules
381      * Only add into deployed modules, as addFieldsToUndeployedLayouts has done this already for undeployed modules (and the admin might have edited the layouts already)
382      * @param string $basepath              Basepath location for this module (not used)
383      * @param string $relationshipName      Name of this relationship (for uniqueness)
384      * @param array $layoutAdditions  An array of module => fieldname
385      * return null
386      */
387     protected function saveFieldsToLayouts ($basepath , $dummy , $relationshipName , $layoutAdditions)
388     {
389         require_once 'modules/ModuleBuilder/parsers/views/GridLayoutMetaDataParser.php' ;
390         
391         // these modules either lack editviews/detailviews or use custom mechanisms for the editview/detailview. In either case, we don't want to attempt to add a relate field to them
392         // would be better if GridLayoutMetaDataParser could handle this gracefully, so we don't have to maintain this list here
393         $invalidModules = array ( 'emails' , 'kbdocuments' ) ;
394         
395         $fieldsToAdd = array();
396         foreach ( $layoutAdditions as $deployedModuleName => $fieldName )
397         {
398             if ( ! in_array( strtolower ( $deployedModuleName ) , $invalidModules ) ) {
399                 foreach ( array ( MB_EDITVIEW , MB_DETAILVIEW ) as $view )
400                 {
401                     $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . ": adding $fieldName to $view layout for module $deployedModuleName" ) ;
402                     $parsedName = self::parseDeployedModuleName ( $deployedModuleName ) ;
403                     if (! isset ( $parsedName [ 'packageName' ] ))
404                     {
405                         $fieldsToAdd [$parsedName [ 'moduleName' ]] = $fieldName;
406                     } 
407                     //Bug 22348: We should add in the field for custom modules not in this package, if they have been deployed.
408                     else if ($parsedName [ 'packageName' ] != $this->packageName 
409                             && isset ( $GLOBALS [ 'beanList' ] [ $deployedModuleName ])){
410                         $fieldsToAdd [$deployedModuleName] = $fieldName;
411                     }
412                 }
413                 }
414         }
415         return array(array('additional_fields' => $fieldsToAdd));
416     }
417
418 }