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-2013 SugarCRM Inc.
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.
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
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
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.
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.
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 ********************************************************************************/
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' ;
44 class UndeployedRelationships extends AbstractRelationships implements RelationshipsInterface
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
54 * Automatically loads in any saved relationships
55 * @param string $path The pathname of the base module directory
57 function __construct ($path)
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 ) ;
72 * Find all modules, deployed and undeployed, that can participate in a relationship
73 * @return array Array of [$module][$subpanel]
75 static function findRelatableModules ()
77 // first find all deployed modules that we might participate in a relationship
78 $relatableModules = parent::findRelatableModules ( ) ;
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...
83 require_once 'modules/ModuleBuilder/MB/ModuleBuilder.php' ;
84 $mb = new ModuleBuilder ( ) ;
86 foreach ( $mb->getPackageList () as $packageName )
88 $package = $mb->packages [ $packageName ] ;
89 foreach ( $package->modules as $module )
91 $relatableModules [ $package->key . "_" . $module->name ] = $module->getProvidedSubpanels () ;
95 return $relatableModules ;
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
105 function add ($relationship)
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()
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()
116 function delete ($relationshipName)
118 if ($relationship = $this->get ( $relationshipName ))
120 $this->removeFieldsFromUndeployedLayouts ( $relationship ) ;
121 unset ( $this->relationships [ $relationshipName ] ) ;
126 * Load the saved relationship definitions for this module
130 $this->relationships = parent::_load ( $this->basepath ) ;
134 * Save this modules relationship definitions out to a working file
138 parent::_save ( $this->relationships, $this->basepath ) ;
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
146 protected function _updateRelationshipDefinition ($definition)
148 if (isset ( $definition [ 'relate' ] ))
150 $newDefinition = array ( ) ;
151 foreach ( array ( 'relate' => 'rhs_module' , 'rsub' => 'rhs_subpanel' , 'msub' => 'lhs_subpanel' , 'label' => 'label' ) as $oldParameter => $newParameter )
153 if (isset ( $definition [ $oldParameter ] ))
155 $definition [ $newParameter ] = $definition [ $oldParameter ] ;
156 unset ( $definition [ $oldParameter ] ) ;
159 $definition [ 'lhs_module' ] = "{$this->packageKey}_{$this->moduleName}" ;
160 // finally update the relationship name
161 unset ( $definition [ 'name' ] ) ; // clear the oldstyle name
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
171 protected function getAllRelationships ()
173 // start with the set of relationships known to this module plus those already deployed
174 $allRelationships = array_merge ( $this->relationships, parent::getDeployedRelationships () ) ;
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 )
182 $package = $mb->packages [ $packageName ] ;
183 foreach ( $package->modules as $module )
186 foreach ( $module->relationships->getRelationshipList () as $relationshipName )
188 $relationship = $module->relationships->get ( $relationshipName ) ;
189 $allRelationships [ $relationship->getName () ] = $relationship->getDefinition () ;
194 return $allRelationships ;
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
204 public function enforceRelationshipOnly ($relationship)
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 )
209 if ($rel->lhs_module == $relationship->lhs_module && $rel->rhs_module == $relationship->rhs_module)
211 $rel->setRelationship_only () ;
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
225 function build ($basepath)
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
233 $relationships = array ( ) ;
234 $this->activitiesToAdd = false ;
235 foreach ( $this->relationships as $relationshipName => $relationship )
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')
241 $this->activitiesToAdd = true ;
242 $relationshipName = $definition [ 'relationship_name' ] ;
243 foreach ( self::$activities as $activitiesSubModuleLower => $activitiesSubModuleName )
245 $definition [ 'rhs_module' ] = $activitiesSubModuleName ;
246 $definition [ 'for_activities' ] = true ;
247 $definition [ 'relationship_name' ] = $relationshipName . '_' . $activitiesSubModuleLower ;
248 $relationships [ $definition [ 'relationship_name' ] ] = RelationshipFactory::newRelationship ( $definition ) ;
253 $relationships [ $definition [ 'relationship_name' ] ] = $relationship ;
257 require_once 'modules/ModuleBuilder/MB/ModuleBuilder.php' ;
258 $mb = new ModuleBuilder ( ) ;
259 $module = $mb->getPackageModule ( $this->packageName, $this->moduleName ) ;
260 if ($this->activitiesToAdd)
262 $appStrings = $module->getAppListStrings () ;
263 foreach(getTypeDisplayList() as $typeDisplay)
265 $appStrings[$typeDisplay][$module->key_name] = $module->getlabel ( 'en_us', 'LBL_MODULE_TITLE' ) ;
267 $module->setAppListStrings ( 'en_us', $appStrings ) ;
273 //Bug42170================================
274 $appStrings = $module->getAppListStrings () ;
275 foreach(getTypeDisplayList() as $typeDisplay)
277 if(isset($appStrings[$typeDisplay][$module->key_name]))
279 unset($appStrings[$typeDisplay][$module->key_name]);
282 $module->setAppListStrings ( 'en_us', $appStrings ) ;
284 //Bug42170================================
287 // use an installDefPrefix of <basepath>/SugarModules for compatibility with the rest of ModuleBuilder
288 $this->installDefs = parent::build ( $basepath, "<basepath>/SugarModules", $relationships ) ;
292 * Add the installDefs for this relationship to the definitions in the parameter
293 * Required by MBModule
294 * @param reference installDef Reference to the set of installDefs to which this relationship's installDefs should be added
296 function addInstallDefs (&$installDef)
298 foreach ( $this->installDefs as $name => $def )
300 if (! empty ( $def ))
302 foreach ( $def as $val )
304 $installDef [ $name ] [] = $val ;
310 private function addFieldsToUndeployedLayouts ($relationship)
312 return $this->updateUndeployedLayout ( $relationship, true ) ;
315 private function removeFieldsFromUndeployedLayouts ($relationship)
317 return $this->updateUndeployedLayout ( $relationship, false ) ;
321 * @param AbstractRelationship $relationship
324 private function removeAppLangStrings($relationship) {
325 $def = $relationship->getDefinition();
326 if (strtolower ( $def [ 'rhs_module' ] ) == 'activities' && !empty($_REQUEST [ 'view_package' ]) && !empty($_REQUEST [ 'view_module' ] ))
328 $mb = new ModuleBuilder ( ) ;
329 $module = $mb->getPackageModule ( $_REQUEST [ 'view_package' ], $_REQUEST [ 'view_module' ] ) ;
330 $appStrings = $module->getAppListStrings () ;
331 foreach(getTypeDisplayList() as $key)
333 if (isset($appStrings[$key][ $module->key_name ]))
334 unset($appStrings[$key][ $module->key_name ]);
336 $module->setAppListStrings ( 'en_us', $appStrings ) ;
342 * Add any relate fields to the DetailView and EditView of the appropriate module immediately (don't wait for a build)
343 * @param AbstractRelationship $relationship The relationship whose fields we are to add or remove
344 * @param boolean $actionAdd True if we are to add; false if to remove
347 private function updateUndeployedLayout ($relationship , $actionAdd = true)
350 // many-to-many relationships don't have fields so if we have a many-to-many we can just skip this...
351 if ($relationship->getType () == MB_MANYTOMANY)
355 $layoutAdditions = $relationship->buildFieldsToLayouts () ;
357 require_once 'modules/ModuleBuilder/parsers/views/GridLayoutMetaDataParser.php' ;
358 foreach ( $layoutAdditions as $deployedModuleName => $fieldName )
360 foreach ( array ( MB_EDITVIEW , MB_DETAILVIEW ) as $view )
362 $parsedName = AbstractRelationships::parseDeployedModuleName ( $deployedModuleName ) ;
363 if (isset ( $parsedName [ 'packageName' ] ))
365 $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . ": " . (($actionAdd) ? "adding" : "removing") . " $fieldName on $view layout for undeployed module {$parsedName [ 'moduleName' ]} in package {$parsedName [ 'packageName' ]}" ) ;
366 $parser = new GridLayoutMetaDataParser ( $view, $parsedName [ 'moduleName' ], $parsedName [ 'packageName' ] ) ;
368 if (($actionAdd) ? $parser->addField ( array ( 'name' => $fieldName ) ) : $parser->removeField ( $fieldName ))
370 $parser->handleSave ( false ) ;
374 $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . ": couldn't " . (($actionAdd) ? "add" : "remove") . " $fieldName on $view layout for undeployed module $deployedModuleName" ) ;
375 $successful = false ;
385 * Add any fields to the DetailView and EditView of the appropriate modules
386 * Only add into deployed modules, as addFieldsToUndeployedLayouts has done this already for undeployed modules (and the admin might have edited the layouts already)
387 * @param string $basepath Basepath location for this module (not used)
388 * @param string $relationshipName Name of this relationship (for uniqueness)
389 * @param array $layoutAdditions An array of module => fieldname
392 protected function saveFieldsToLayouts ($basepath , $dummy , $relationshipName , $layoutAdditions)
394 require_once 'modules/ModuleBuilder/parsers/views/GridLayoutMetaDataParser.php' ;
396 // 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
397 // would be better if GridLayoutMetaDataParser could handle this gracefully, so we don't have to maintain this list here
398 $invalidModules = array ( 'emails' , 'kbdocuments' ) ;
400 $fieldsToAdd = array();
401 foreach ( $layoutAdditions as $deployedModuleName => $fieldName )
403 if ( ! in_array( strtolower ( $deployedModuleName ) , $invalidModules ) ) {
404 foreach ( array ( MB_EDITVIEW , MB_DETAILVIEW ) as $view )
406 $GLOBALS [ 'log' ]->debug ( get_class ( $this ) . ": adding $fieldName to $view layout for module $deployedModuleName" ) ;
407 $parsedName = self::parseDeployedModuleName ( $deployedModuleName ) ;
408 if (! isset ( $parsedName [ 'packageName' ] ))
410 $fieldsToAdd [$parsedName [ 'moduleName' ]] = $fieldName;
412 //Bug 22348: We should add in the field for custom modules not in this package, if they have been deployed.
413 else if ($parsedName [ 'packageName' ] != $this->packageName
414 && isset ( $GLOBALS [ 'beanList' ] [ $deployedModuleName ])){
415 $fieldsToAdd [$deployedModuleName] = $fieldName;
420 return array(array('additional_fields' => $fieldsToAdd));