Exemple #1
0
 private function createRelationship($lhs_module, $rhs_module = null, $relationship_type = 'one-to-many')
 {
     $rhs_module = $rhs_module == null ? $lhs_module : $rhs_module;
     // Adding relation between products and users
     $this->relationships = new DeployedRelationships($lhs_module);
     $definition = array('lhs_module' => $lhs_module, 'relationship_type' => $relationship_type, 'rhs_module' => $rhs_module, 'lhs_label' => $lhs_module, 'rhs_label' => $rhs_module, 'rhs_subpanel' => 'default');
     $this->relationship = RelationshipFactory::newRelationship($definition);
     $this->relationships->add($this->relationship);
     $this->relationships->save();
     $this->relationships->build();
     LanguageManager::clearLanguageCache($lhs_module);
     // Updating $dictionary by created relation
     global $dictionary;
     $moduleInstaller = new ModuleInstaller();
     $moduleInstaller->silent = true;
     $moduleInstaller->rebuild_tabledictionary();
     require 'modules/TableDictionary.php';
     // Updating vardefs
     VardefManager::$linkFields = array();
     VardefManager::clearVardef();
     VardefManager::refreshVardefs($lhs_module, BeanFactory::getObjectName($lhs_module));
     if ($lhs_module != $rhs_module) {
         VardefManager::refreshVardefs($rhs_module, BeanFactory::getObjectName($rhs_module));
     }
     SugarRelationshipFactory::rebuildCache();
 }
Exemple #2
0
 public function setUp()
 {
     SugarTestHelper::setUp('current_user', array(true, 1));
     SugarTestHelper::setUp('app_list_strings');
     SugarTestHelper::setUp('beanFiles');
     SugarTestHelper::setUp('beanList');
     parent::setUp();
     $this->relationships = new DeployedRelationships('Products');
     $definition = array('lhs_module' => 'Accounts', 'relationship_type' => 'one-to-many', 'rhs_module' => 'ProjectTask');
     $this->relationship = RelationshipFactory::newRelationship($definition);
     $this->relationships->add($this->relationship);
     $this->relationships->save();
     $this->relationships->build();
     SugarTestHelper::setUp('relation', array('Accounts', 'ProjectTask'));
     $searchDefs = array('layout' => array('advanced_search' => array($this->relationship->getName() . '_name' => array('type' => 'relate', 'link' => true, 'label' => '', 'id' => strtoupper($this->relationship->getJoinKeyLHS()), 'width' => '10%', 'default' => true, 'name' => $this->relationship->getName() . '_name'))), 'templateMeta' => array('maxColumns' => '3', 'maxColumnsBasic' => '4', 'widths' => array('label' => '10', 'field' => '30')));
     // Add new field to advanced search layout
     if (file_exists("custom/modules/ProjectTask/metadata/searchdefs.php")) {
         $this->_savedSearchDefs = file_get_contents("custom/modules/ProjectTask/metadata/searchdefs.php");
     }
     write_array_to_file("searchdefs['ProjectTask']", $searchDefs, 'custom/modules/ProjectTask/metadata/searchdefs.php');
     if (file_exists("modules/ProjectTask/metadata/SearchFields.php")) {
         $this->_savedSearchFields = file_get_contents("modules/ProjectTask/metadata/SearchFields.php");
     }
     write_array_to_file("searchFields['ProjectTask']", $this->_localSearchFields['ProjectTask'], 'modules/ProjectTask/metadata/SearchFields.php');
     // Creates linked test account, project and project task
     $this->_project = SugarTestProjectUtilities::createProject();
     $this->_account = SugarTestAccountUtilities::createAccount();
     $projectTaskData = array('project_id' => $this->_project->id, 'parent_task_id' => '', 'project_task_id' => '1', 'percent_complete' => '0', 'name' => 'Test Task 1', 'duration_unit' => 'Days', 'duration' => '1');
     $this->_projectTask = SugarTestProjectTaskUtilities::createProjectTask($projectTaskData);
     $this->_projectTask->{$this->relationship->getName()}->add($this->_account);
     $this->_projectTask->save();
 }
 protected function _load($basepath)
 {
     $GLOBALS['log']->info(get_class($this) . ": loading relationships from " . $basepath . '/relationships.php');
     $objects = array();
     if (file_exists($basepath . '/relationships.php')) {
         include $basepath . '/relationships.php';
         foreach ($relationships as $name => $definition) {
             // update any pre-5.1 relationships to the new definitions
             // 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
             // 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
             $definition = $this->_updateRelationshipDefinition($definition);
             $relationship = RelationshipFactory::newRelationship($definition);
             // make sure it has a unique name
             if (!isset($definition['relationship_name'])) {
                 $name = $this->getUniqueName($relationship);
                 $relationship->setName($name);
             }
             $objects[$name] = $relationship;
         }
     }
     return $objects;
 }
 function display()
 {
     $selected_lang = !empty($_REQUEST['relationship_lang']) ? $_REQUEST['relationship_lang'] : $_SESSION['authenticated_user_language'];
     $this->smarty = new Sugar_Smarty();
     $ac = new AjaxCompose();
     $this->fromModuleBuilder = isset($_REQUEST['MB']) || !empty($_REQUEST['view_package']) && $_REQUEST['view_package'] != 'studio';
     $this->smarty->assign('fromModuleBuilder', $this->fromModuleBuilder);
     if (!$this->fromModuleBuilder) {
         $module = StudioModuleFactory::getStudioModule($_REQUEST['view_module']);
         $moduleName = $_REQUEST['view_module'];
         $fields = $module->fields;
         require_once 'modules/ModuleBuilder/parsers/relationships/DeployedRelationships.php';
         $relatableModules = DeployedRelationships::findRelatableModules();
         $appStrings = return_app_list_strings_language($selected_lang);
         $modStrings = return_module_language($selected_lang, $_REQUEST['view_module'], true);
         $appStrings = $appStrings['moduleList'];
     } else {
         $mb = new ModuleBuilder();
         $mb->getPackages();
         //display the latest module name rather than what is in or not in the loaded app_list_strings.
         $mb->getPackage($_REQUEST['view_package'])->loadModuleTitles();
         $module = $mb->getPackageModule($_REQUEST['view_package'], $_REQUEST['view_module']);
         $moduleName = empty($module->key_name) ? $module->getModuleName() : $module->key_name;
         $this->smarty->assign('view_package', $_REQUEST['view_package']);
         $mbvardefs = $module->getVardefs();
         $fields = $mbvardefs['fields'];
         require_once 'modules/ModuleBuilder/parsers/relationships/UndeployedRelationships.php';
         $relatableModules = UndeployedRelationships::findRelatableModules();
         $appStrings = $module->getModStrings($selected_lang);
     }
     ksort($relatableModules);
     $lhs_subpanels = $module->getProvidedSubpanels();
     // Fix to re-add sorting of the subpanel names so that the 'default' subpanel always appears first in the list.
     // This assumes that subpanels are usually named ForXYZ which is the case currently, and hence 'default' will be sorted first.
     //I f this assumption is incorrect, then a better solution would be to remove 'default' from the subpanel list, then sort, and finally array_unshift it back on.
     natcasesort($lhs_subpanels);
     $cardinality = array(MB_ONETOONE => translate('LBL_ONETOONE'), MB_ONETOMANY => translate('LBL_ONETOMANY'), MB_MANYTOONE => translate('LBL_MANYTOONE'), MB_MANYTOMANY => translate('LBL_MANYTOMANY'));
     if (!$this->fromModuleBuilder) {
         unset($cardinality[MB_MANYTOONE]);
     }
     $relationships = $module->getRelationships();
     // if a description for this relationship already exists, then load it so it can be modified
     if (!empty($_REQUEST['relationship_name'])) {
         $relationship = $relationships->get($_REQUEST['relationship_name']);
         $relationship->setName($_REQUEST['relationship_name']);
         $definition = $relationship->getDefinition();
         if (!$this->fromModuleBuilder) {
             $modStrings = return_module_language($selected_lang, $relationship->rhs_module, true);
             $definition['lhs_label'] = isset($modStrings[$relationship->getTitleKey()]) ? $modStrings[$relationship->getTitleKey()] : $relationship->lhs_module;
             $modStrings = return_module_language($selected_lang, $relationship->lhs_module, true);
             $definition['rhs_label'] = isset($modStrings[$relationship->getTitleKey(true)]) ? $modStrings[$relationship->getTitleKey(true)] : $relationship->rhs_module;
         } else {
             #30624
             if (!empty($_REQUEST['rhs_module'])) {
                 $definition['rhs_label'] = $_REQUEST['rhs_module'];
             }
         }
     } else {
         $definition = array();
         $firstModuleDefinition = each($relatableModules);
         $definition['rhs_module'] = $firstModuleDefinition['key'];
         $definition['lhs_module'] = $moduleName;
         $definition['lhs_label'] = translate($moduleName);
         $definition['relationship_type'] = MB_MANYTOMANY;
     }
     // load the relationship from post - required as we can call view.relationship.php from Ajax when changing the rhs_module for example
     $definition = $this->overrideDefinitionFromPOST($definition);
     if (empty($definition['rhs_label'])) {
         $definition['rhs_label'] = translate($definition['rhs_module']);
     }
     if (empty($definition['lhs_label'])) {
         $definition['lhs_label'] = translate($definition['lhs_module']);
     }
     $relationship = RelationshipFactory::newRelationship($definition);
     $rhs_subpanels = $relatableModules[$relationship->rhs_module];
     // Fix to re-add sorting of the subpanel names so that the 'default' subpanel always appears first in the list. This assumes that subpanels are usually named ForXYZ which is the case currently, and hence 'default' will be sorted first. If this assumption is incorrect, then a better solution would be to remove 'default' from the subpanel list, then sort, and finally array_unshift it back on.
     natcasesort($rhs_subpanels);
     if (empty($_REQUEST['relationship_name'])) {
         // tidy up the options for the view based on the modules participating in the relationship and the cardinality
         // some modules (e.g., Knowledge Base/KBDocuments) lack subpanels. That means they can't be the lhs of a 1-many or many-many, or the rhs of a many-many for example
         // fix up the available cardinality options
         $relationship_type = $relationship->getType();
         if (count($lhs_subpanels) == 0 || count($rhs_subpanels) == 0) {
             unset($cardinality[MB_MANYTOMANY]);
         }
         if (count($rhs_subpanels) == 0) {
             unset($cardinality[MB_ONETOMANY]);
         }
         if (isset($definition['rhs_module']) && $definition['rhs_module'] == 'Activities') {
             $cardinality = array(MB_ONETOMANY => translate('LBL_ONETOMANY'));
         }
         //Bug 23139, Campaigns module current cannot display custom subpanels, so we need to ban it from any
         //relationships that would require a new subpanel to be shown in Campaigns.
         if (isset($definition['lhs_module']) && $definition['lhs_module'] == 'Campaigns') {
             unset($cardinality[MB_MANYTOMANY]);
             unset($cardinality[MB_ONETOMANY]);
         }
         if (isset($definition['rhs_module']) && $definition['rhs_module'] == 'Campaigns' && isset($cardinality[MB_MANYTOMANY])) {
             unset($cardinality[MB_MANYTOMANY]);
             unset($cardinality[MB_MANYTOONE]);
         }
         if (!isset($cardinality[$relationship->getType()])) {
             end($cardinality);
             $definition['relationship_type'] = key($cardinality);
             $relationship = RelationshipFactory::newRelationship($definition);
         }
         $this->smarty->assign('is_new', true);
     } else {
         $this->smarty->assign('is_new', false);
     }
     //Remove Activities if one-to-many is not availible
     if (!isset($cardinality[MB_ONETOMANY]) && isset($relatableModules['Activities'])) {
         unset($relatableModules['Activities']);
     }
     // now enforce the relationship_only requirement - that is, only construct the underlying relationship and link fields, and not the UI, if the subpanel code will have troubles displaying the UI
     $relationships->enforceRelationshipOnly($relationship);
     $this->smarty->assign('view_module', $_REQUEST['view_module']);
     $this->smarty->assign('rel', $relationship->getDefinition());
     $this->smarty->assign('mod_strings', $GLOBALS['mod_strings']);
     $this->smarty->assign('module_key', $relationship->lhs_module);
     $this->smarty->assign('cardinality', array_keys($cardinality));
     $this->smarty->assign('translated_cardinality', $cardinality);
     $this->smarty->assign('selected_cardinality', translate($relationship->getType()));
     $relatable = array();
     foreach ($relatableModules as $name => $dummy) {
         $relatable[$name] = translate($name);
     }
     unset($relatable['KBDocuments']);
     natcasesort($relatable);
     $this->smarty->assign('relatable', array_keys($relatable));
     $this->smarty->assign('translated_relatable', $relatable);
     $this->smarty->assign('rhspanels', $rhs_subpanels);
     $this->smarty->assign('lhspanels', $lhs_subpanels);
     $this->smarty->assign('selected_lang', $selected_lang);
     $this->smarty->assign('available_languages', get_languages());
     switch ($relationship->relationship_type) {
         case MB_ONETOONE:
             break;
         case MB_ONETOMANY:
             if (empty($relationship->relationship_column_name)) {
                 $validRoleColumnFields = array();
                 foreach ($fields as $field) {
                     $validRoleColumnFields[] = $field;
                 }
                 $this->smarty->assign('relationship_role_column_enum', $validRoleColumnFields);
             }
             if (!empty($relationship->relationship_role_column_value)) {
                 $this->smarty->assign('relationship_role_column_value', $relationship->relationship_role_column_value);
             }
             break;
         case MB_MANYTOMANY:
             if (!empty($relationship->relationship_role_column_value)) {
                 $this->smarty->assign('relationship_role_column_value', $relationship->relationship_role_column_value);
             }
             break;
     }
     //see if we use the new system
     if (isset($_REQUEST['json']) && $_REQUEST['json'] == 'false') {
         echo $this->smarty->fetch('modules/ModuleBuilder/tpls/studioRelationship.tpl');
     } else {
         $ac->addSection('east', $module->name . ' ' . $GLOBALS['mod_strings']['LBL_RELATIONSHIPS'], $this->smarty->fetch('modules/ModuleBuilder/tpls/studioRelationship.tpl'));
         echo $ac->getJavascript();
     }
 }
 function build()
 {
     $basepath = "custom/Extension/modules";
     $this->activitiesToAdd = false;
     // and mark all as built so that the next time we come through we'll know and won't build again
     foreach ($this->relationships as $name => $relationship) {
         $definition = $relationship->getDefinition();
         // activities will always appear on the rhs only - lhs will be always be this module in MB
         if (strtolower($definition['rhs_module']) == 'activities') {
             $this->activitiesToAdd = true;
             $relationshipName = $definition['relationship_name'];
             foreach (self::$activities as $activitiesSubModuleLower => $activitiesSubModuleName) {
                 $definition['rhs_module'] = $activitiesSubModuleName;
                 $definition['for_activities'] = true;
                 $definition['relationship_name'] = $relationshipName . '_' . $activitiesSubModuleLower;
                 $this->relationships[$definition['relationship_name']] = RelationshipFactory::newRelationship($definition);
             }
             unset($this->relationships[$name]);
         }
     }
     $GLOBALS['log']->info(get_class($this) . "->build(): installing relationships");
     $MBModStrings = $GLOBALS['mod_strings'];
     $adminModStrings = return_module_language('', 'Administration');
     // required by ModuleInstaller
     foreach ($this->relationships as $name => $relationship) {
         $relationship->setFromStudio();
         $GLOBALS['mod_strings'] = $MBModStrings;
         $installDefs = parent::build($basepath, "<basepath>", array($name => $relationship));
         // and mark as built so that the next time we come through we'll know and won't build again
         $relationship->setReadonly();
         $this->relationships[$name] = $relationship;
         // now install the relationship - ModuleInstaller normally only does this as part of a package load where it installs the relationships defined in the manifest. However, we don't have a manifest or a package, so...
         // If we were to chose to just use the Extension mechanism, without using the ModuleInstaller install_...() methods, we must :
         // 1)   place the information for each side of the relationship in the appropriate Ext directory for the module, which means specific $this->save...() methods for DeployedRelationships, and
         // 2)   we must also manually add the relationship into the custom/application/Ext/TableDictionary/tabledictionary.ext.php file as install_relationship doesn't handle that (install_relationships which requires the manifest however does)
         //      Relationships must be in tabledictionary.ext.php for the Admin command Rebuild Relationships to reliably work:
         //      Rebuild Relationships looks for relationships in the modules vardefs.php, in custom/modules/<modulename>/Ext/vardefs/vardefs.ext.php, and in modules/TableDictionary.php and custom/application/Ext/TableDictionary/tabledictionary.ext.php
         //      if the relationship is not defined in one of those four places it could be deleted during a rebuilt, or during a module installation (when RebuildRelationships.php deletes all entries in the Relationships table)
         // So instead of doing this, we use common save...() methods between DeployedRelationships and UndeployedRelationships that will produce installDefs,
         // and rather than building a full manifest file to carry them, we manually add these installDefs to the ModuleInstaller, and then
         // individually call the appropriate ModuleInstaller->install_...() methods to take our relationship out of our staging area and expand it out to the individual module Ext areas
         $GLOBALS['mod_strings'] = $adminModStrings;
         require_once 'ModuleInstall/ModuleInstaller.php';
         $mi = new ModuleInstaller();
         $mi->id_name = 'custom' . $name;
         // provide the moduleinstaller with a unique name for this relationship - normally this value is set to the package key...
         $mi->installdefs = $installDefs;
         $mi->base_dir = $basepath;
         $mi->silent = true;
         VardefManager::clearVardef();
         $mi->install_relationships();
         $mi->install_languages();
         $mi->install_vardefs();
         $mi->install_layoutdefs();
     }
     // now clear all caches so that our changes are visible
     require_once 'modules/Administration/QuickRepairAndRebuild.php';
     $rac = new RepairAndClear();
     $rac->repairAndClearAll(array('clearAll'), array($GLOBALS['mod_strings']['LBL_ALL_MODULES']), true, false);
     $GLOBALS['mod_strings'] = $MBModStrings;
     // finally, restore the ModuleBuilder mod_strings
     // save out the updated definitions so that we keep track of the change in built status
     $this->save();
     $GLOBALS['log']->info(get_class($this) . "->build(): finished relationship installation");
 }
 function add($rel)
 {
     // convert old format definition to new format
     if (!isset($rel['lhs_module'])) {
         $rel['lhs_module'] = $this->moduleName;
     }
     $definition = AbstractRelationships::convertFromOldFormat($rel);
     if (!isset($definition['relationship_type'])) {
         $definition['relationship_type'] = 'many-to-many';
     }
     // get relationship object from RelationshipFactory
     $relationship = RelationshipFactory::newRelationship($definition);
     // add relationship to the set of relationships
     $this->implementation->add($relationship);
     $this->updateRelationshipVariable();
     return $relationship;
 }
 function build($basepath)
 {
     // first expand out any reference to Activities to its submodules
     // 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
     // as the relationship name, that must be unique
     // 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
     $relationships = array();
     $this->activitiesToAdd = false;
     foreach ($this->relationships as $relationshipName => $relationship) {
         $definition = $relationship->getDefinition();
         // activities will always appear on the rhs only - lhs will be always be this module in MB
         if (strtolower($definition['rhs_module']) == 'activities') {
             $this->activitiesToAdd = true;
             $relationshipName = $definition['relationship_name'];
             foreach (self::$activities as $activitiesSubModuleLower => $activitiesSubModuleName) {
                 $definition['rhs_module'] = $activitiesSubModuleName;
                 $definition['for_activities'] = true;
                 $definition['relationship_name'] = $relationshipName . '_' . $activitiesSubModuleLower;
                 $relationships[$definition['relationship_name']] = RelationshipFactory::newRelationship($definition);
             }
         } else {
             $relationships[$definition['relationship_name']] = $relationship;
         }
     }
     require_once 'modules/ModuleBuilder/MB/ModuleBuilder.php';
     $mb = new ModuleBuilder();
     $module = $mb->getPackageModule($this->packageName, $this->moduleName);
     if ($this->activitiesToAdd) {
         $appStrings = $module->getAppListStrings();
         $appStrings['parent_type_display'][$module->key_name] = $module->getlabel('en_us', 'LBL_MODULE_TITLE');
         $appStrings['record_type_display'][$module->key_name] = $module->getlabel('en_us', 'LBL_MODULE_TITLE');
         $appStrings['record_type_display_notes'][$module->key_name] = $module->getlabel('en_us', 'LBL_MODULE_TITLE');
         $module->setAppListStrings('en_us', $appStrings);
         $module->save();
     }
     // use an installDefPrefix of <basepath>/SugarModules for compatibility with the rest of ModuleBuilder
     $this->installDefs = parent::build($basepath, "<basepath>/SugarModules", $relationships);
 }
 function build()
 {
     $modulesToBuild = array();
     if (!isset($this->relationships[$this->newRelationshipName])) {
         $GLOBALS['log']->fatal("Could not find a relationship by the name of {$this->newRelationshipName}, you will have to quick repair and rebuild to get the new relationship added.");
     } else {
         $newRel = $this->relationships[$this->newRelationshipName];
         $newRelDef = $newRel->getDefinition();
         $modulesToBuild[$newRelDef['rhs_module']] = true;
         $modulesToBuild[$newRelDef['lhs_module']] = true;
     }
     $basepath = "custom/Extension/modules";
     $this->activitiesToAdd = false;
     // and mark all as built so that the next time we come through we'll know and won't build again
     foreach ($this->relationships as $name => $relationship) {
         if ($relationship->readonly()) {
             continue;
         }
         $definition = $relationship->getDefinition();
         // activities will always appear on the rhs only - lhs will be always be this module in MB
         if (strtolower($definition['rhs_module']) == 'activities') {
             $this->activitiesToAdd = true;
             $relationshipName = $definition['relationship_name'];
             foreach (self::$activities as $activitiesSubModuleLower => $activitiesSubModuleName) {
                 $definition['rhs_module'] = $activitiesSubModuleName;
                 $definition['for_activities'] = true;
                 $definition['relationship_name'] = $relationshipName . '_' . $activitiesSubModuleLower;
                 $this->relationships[$definition['relationship_name']] = RelationshipFactory::newRelationship($definition);
             }
             unset($this->relationships[$name]);
         }
     }
     $GLOBALS['log']->info(get_class($this) . "->build(): installing relationships");
     $MBModStrings = $GLOBALS['mod_strings'];
     $adminModStrings = return_module_language('', 'Administration');
     // required by ModuleInstaller
     foreach ($this->relationships as $name => $relationship) {
         if ($relationship->readonly()) {
             continue;
         }
         $relationship->setFromStudio();
         $GLOBALS['mod_strings'] = $MBModStrings;
         $installDefs = parent::build($basepath, "<basepath>", array($name => $relationship));
         // and mark as built so that the next time we come through we'll know and won't build again
         $relationship->setReadonly();
         $this->relationships[$name] = $relationship;
         // now install the relationship - ModuleInstaller normally only does this as part of a package load where it installs the relationships defined in the manifest. However, we don't have a manifest or a package, so...
         // If we were to chose to just use the Extension mechanism, without using the ModuleInstaller install_...() methods, we must :
         // 1)   place the information for each side of the relationship in the appropriate Ext directory for the module, which means specific $this->save...() methods for DeployedRelationships, and
         // 2)   we must also manually add the relationship into the custom/application/Ext/TableDictionary/tabledictionary.ext.php file as install_relationship doesn't handle that (install_relationships which requires the manifest however does)
         //      Relationships must be in tabledictionary.ext.php for the Admin command Rebuild Relationships to reliably work:
         //      Rebuild Relationships looks for relationships in the modules vardefs.php, in custom/modules/<modulename>/Ext/vardefs/vardefs.ext.php, and in modules/TableDictionary.php and custom/application/Ext/TableDictionary/tabledictionary.ext.php
         //      if the relationship is not defined in one of those four places it could be deleted during a rebuilt, or during a module installation (when RebuildRelationships.php deletes all entries in the Relationships table)
         // So instead of doing this, we use common save...() methods between DeployedRelationships and UndeployedRelationships that will produce installDefs,
         // and rather than building a full manifest file to carry them, we manually add these installDefs to the ModuleInstaller, and then
         // individually call the appropriate ModuleInstaller->install_...() methods to take our relationship out of our staging area and expand it out to the individual module Ext areas
         $GLOBALS['mod_strings'] = $adminModStrings;
         require_once 'ModuleInstall/ModuleInstaller.php';
         $mi = new ModuleInstaller();
         $mi->id_name = 'custom' . $name;
         // provide the moduleinstaller with a unique name for this relationship - normally this value is set to the package key...
         $mi->installdefs = $installDefs;
         $mi->base_dir = $basepath;
         $mi->silent = true;
         VardefManager::clearVardef();
         $mi->install_relationships();
         $mi->install_languages();
         $mi->install_vardefs();
         $mi->install_layoutdefs();
         $mi->install_extensions();
         $mi->install_client_files();
     }
     $GLOBALS['mod_strings'] = $MBModStrings;
     // finally, restore the ModuleBuilder mod_strings
     // Anything that runs in-process needs to reload their vardefs
     $GLOBALS['reload_vardefs'] = true;
     // save out the updated definitions so that we keep track of the change in built status
     // sending false so we don't rebuild relationshsips for a third time.
     $this->save(false);
     $mi = new ModuleInstaller();
     $mi->silent = true;
     $mi->rebuild_relationships($modulesToBuild);
     // now clear all caches so that our changes are visible
     require_once 'modules/Administration/QuickRepairAndRebuild.php';
     $rac = new RepairAndClear();
     $rac->module_list = $modulesToBuild;
     $rac->clearJsFiles();
     $rac->clearVardefs();
     $rac->clearJsLangFiles();
     $rac->clearLanguageCache();
     $rac->rebuildExtensions(array_keys($modulesToBuild));
     $rac->clearVardefs();
     foreach ($rac->module_list as $moduleName => $ignore) {
         // Now rebuild the vardefs in memory
         $bean = BeanFactory::newBean($moduleName);
         VardefManager::loadVardef($bean->module_dir, $bean->object_name, true, array('bean' => $bean));
     }
     foreach (array_keys($modulesToBuild) as $module) {
         unset($GLOBALS['dictionary'][BeanFactory::getObjectName($module)]);
     }
     SugarRelationshipFactory::rebuildCache();
     MetaDataManager::refreshLanguagesCache(array($GLOBALS['current_language']));
     MetaDataManager::refreshSectionCache(array(MetaDataManager::MM_RELATIONSHIPS));
     MetaDataManager::refreshModulesCache(array_keys($modulesToBuild));
     $GLOBALS['log']->info(get_class($this) . "->build(): finished relationship installation");
 }
/**
 * Searches through the installed relationships to find broken self referencing one-to-many relationships 
 * (wrong field used in the subpanel, and the left link not marked as left)
 */
function upgrade_custom_relationships($modules = array())
{
    global $current_user, $moduleList;
    if (!is_admin($current_user)) {
        sugar_die($GLOBALS['app_strings']['ERR_NOT_ADMIN']);
    }
    require_once "modules/ModuleBuilder/parsers/relationships/DeployedRelationships.php";
    require_once "modules/ModuleBuilder/parsers/relationships/OneToManyRelationship.php";
    if (empty($modules)) {
        $modules = $moduleList;
    }
    foreach ($modules as $module) {
        $depRels = new DeployedRelationships($module);
        $relList = $depRels->getRelationshipList();
        foreach ($relList as $relName) {
            $relObject = $depRels->get($relName);
            $def = $relObject->getDefinition();
            //We only need to fix self referencing one to many relationships
            if ($def['lhs_module'] == $def['rhs_module'] && $def['is_custom'] && $def['relationship_type'] == "one-to-many") {
                $layout_defs = array();
                if (!is_dir("custom/Extension/modules/{$module}/Ext/Layoutdefs") || !is_dir("custom/Extension/modules/{$module}/Ext/Vardefs")) {
                    continue;
                }
                //Find the extension file containing the vardefs for this relationship
                foreach (scandir("custom/Extension/modules/{$module}/Ext/Vardefs") as $file) {
                    if (substr($file, 0, 1) != "." && strtolower(substr($file, -4)) == ".php") {
                        $dictionary = array($module => array("fields" => array()));
                        $filePath = "custom/Extension/modules/{$module}/Ext/Vardefs/{$file}";
                        include $filePath;
                        if (isset($dictionary[$module]["fields"][$relName])) {
                            $rhsDef = $dictionary[$module]["fields"][$relName];
                            //Update the vardef for the left side link field
                            if (!isset($rhsDef['side']) || $rhsDef['side'] != 'left') {
                                $rhsDef['side'] = 'left';
                                $rhsDef['link-type'] = 'one';
                                $fileContents = file_get_contents($filePath);
                                $out = preg_replace('/\\$dictionary[\\w"\'\\[\\]]*?' . $relName . '["\'\\[\\]]*?\\s*?=\\s*?array\\s*?\\(.*?\\);/s', '$dictionary["' . $module . '"]["fields"]["' . $relName . '"]=' . var_export_helper($rhsDef) . ";", $fileContents);
                                file_put_contents($filePath, $out);
                            }
                        }
                    }
                }
                //Find the extension file containing the subpanel definition for this relationship
                foreach (scandir("custom/Extension/modules/{$module}/Ext/Layoutdefs") as $file) {
                    if (substr($file, 0, 1) != "." && strtolower(substr($file, -4)) == ".php") {
                        $layout_defs = array($module => array("subpanel_setup" => array()));
                        $filePath = "custom/Extension/modules/{$module}/Ext/Layoutdefs/{$file}";
                        include $filePath;
                        $bean = BeanFactory::getBean($module);
                        $fields = $bean->getFieldDefinitions();
                        foreach ($layout_defs[$module]["subpanel_setup"] as $key => $subDef) {
                            if (isset($layout_defs[$module]["subpanel_setup"][$key]['get_subpanel_data']) && $layout_defs[$module]["subpanel_setup"][$key]['get_subpanel_data'] == $relName && isset($fields[$relName]) && $fields[$relName]['type'] != 'link') {
                                $fileContents = file_get_contents($filePath);
                                $out = preg_replace('/[\'"]get_subpanel_data[\'"]\\s*=>\\s*[\'"]' . $relName . '[\'"],/s', "'get_subpanel_data' => '{$def["join_key_lhs"]}',", $fileContents);
                                file_put_contents($filePath, $out);
                            }
                        }
                    }
                }
            }
        }
    }
    // Phase 2: Module builder has been incorrectly adding the id
    // field attributes to created relationships
    foreach (glob('custom/Extension/modules/*/Ext/Vardefs/*.php') as $fileToFix) {
        // continue to the next if it's not an existing file or it's a directory
        if (!file_exists($fileToFix) || is_dir($fileToFix)) {
            continue;
        }
        $filename = basename($fileToFix);
        $dictionary = array();
        require $fileToFix;
        $tmp = array_keys($dictionary);
        if (count($tmp) < 1) {
            // Empty dictionary
            continue;
        }
        $dictKey = $tmp[0];
        if (!isset($dictionary[$dictKey]['fields'])) {
            // Not modifying any fields, this isn't a relationship
            continue;
        }
        $isBadRelate = false;
        $idName = '';
        $linkField = null;
        $relateField = null;
        foreach ($dictionary[$dictKey]['fields'] as $fieldName => $field) {
            if (isset($field['id_name']) && $fieldName != $field['id_name']) {
                if (isset($field['type']) && $field['type'] == 'link') {
                    // This looks promising
                    if (isset($dictionary[$dictKey]['fields'][$field['id_name']])) {
                        $idField = $dictionary[$dictKey]['fields'][$field['id_name']];
                        if (isset($idField['type']) && $idField['type'] == 'link') {
                            // This looks like a winner
                            $idName = $field['id_name'];
                            $isBadRelate = true;
                            $linkField = $field;
                        }
                    }
                }
                if (isset($field['type']) && $field['type'] == 'relate') {
                    $relateField = $field;
                }
            }
        }
        if (!$isBadRelate) {
            continue;
        }
        $depRels = new DeployedRelationships($dictKey);
        $relObj = $depRels->get($linkField['relationship']);
        if (!$relObj) {
            // The system doesn't know about the relationship object.
            $linkMetadataLocation = 'custom/metadata/' . $linkField['relationship'] . 'MetaData.php';
            if (file_exists($linkMetadataLocation)) {
                require $linkMetadataLocation;
                $linkDef = $dictionary[$linkField['relationship']];
                $relObj = RelationshipFactory::newRelationship($linkDef);
            }
        }
        $newIdField = array('name' => $idName, 'type' => 'id', 'source' => 'non-db', 'vname' => $idField['vname'], 'id_name' => $idName, 'link' => $relateField['link'], 'table' => $relateField['table'], 'module' => $relateField['module'], 'rname' => 'id', 'reportable' => false, 'massupdate' => false, 'duplicate_merge' => 'disabled', 'hideacl' => true);
        if ($relObj && $relObj->getLhsModule() == $relObj->getRhsModule()) {
            $selfReferencing = true;
        } else {
            $selfReferencing = false;
        }
        if ($selfReferencing) {
            $newLinkField = array('name' => $relateField['link'] . '_right', 'type' => 'link', 'relationship' => $linkField['relationship'], 'source' => 'non-db', 'vname' => $idField['vname'], 'id_name' => $relObj->getJoinKeyRHS(), 'side' => 'right', 'link-type' => 'many');
        }
        $replaceString = '$dictionary["' . $dictKey . '"]["fields"]["' . $idName . '"]=' . var_export_helper($newIdField) . ";\n";
        if ($selfReferencing) {
            $replaceString .= '$dictionary["' . $dictKey . '"]["fields"]["' . $newLinkField['name'] . '"]=' . var_export_helper($newLinkField) . ";\n";
        }
        $fileContents = file_get_contents($fileToFix);
        $out = preg_replace('/\\$dictionary[\\w"\'\\[\\]]*?' . $idName . '["\'\\[\\]]*?\\s*?=\\s*?array\\s*?\\(.*?\\);/s', $replaceString, $fileContents);
        if ($selfReferencing) {
            $out = preg_replace('/\\$dictionary[\\w"\'\\[\\]]*?' . $relateField['name'] . '["\'\\[\\]]*?\\s*?=\\s*?array\\s*?\\(.*?\\);/s', '$dictionary["' . $dictKey . '"]["fields"]["' . $relateField['name'] . '"]=' . var_export_helper($relateField) . ";\n", $out);
        }
        file_put_contents($fileToFix, $out);
        if ($selfReferencing) {
            // Now to fix bad layouts in self-linking relationships
            // Go to the Layoutdefs path
            $layoutPath = dirname(dirname($fileToFix)) . '/Layoutdefs';
            foreach (glob($layoutPath . '/*.php') as $layoutToCheck) {
                // See if they match the id I just changed.
                $layout_defs = array();
                include $layoutToCheck;
                if (isset($layout_defs[$dictKey]['subpanel_setup'][$newIdField['name']])) {
                    $newLayout[$dictKey]['subpanel_setup'][$relateField['link']] = $layout_defs[$dictKey]['subpanel_setup'][$newIdField['name']];
                    $newLayout[$dictKey]['subpanel_setup'][$relateField['link']]['get_subpanel_data'] = $newLinkField['relationship'] . '_right';
                    write_array_to_file('layout_defs', $newLayout, $layoutToCheck);
                }
            }
        }
    }
}