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"); }
protected function getRelateFieldDefinition($sourceModule, $relationshipName, $vnameLabel = '') { $vardef = array(); $vardef['name'] = $relationshipName . "_name"; // must end in _name for the QuickSearch code in TemplateHandler->createQuickSearchCode $vardef['type'] = 'relate'; $vardef['source'] = 'non-db'; if (!empty($vnameLabel)) { $vardef['vname'] = 'LBL_' . strtoupper($relationshipName . '_FROM_' . $vnameLabel) . '_TITLE'; } else { $vardef['vname'] = 'LBL_' . strtoupper($relationshipName . '_FROM_' . $sourceModule) . '_TITLE'; } $vardef['save'] = true; // the magic value to tell SugarBean to save this relate field even though it is not listed in the $relationship_fields array // id_name matches the join_key_ column in the relationship table for the sourceModule - that is, the column in the relationship table containing the id of the corresponding field in the source module's table (vardef['table']) $vardef['id_name'] = $this->getIDName($sourceModule); // link cannot match id_name otherwise the $bean->$id_name value set from the POST is overwritten by the Link object created by this 'link' entry $vardef['link'] = $relationshipName; // the name of the link field that points to the relationship - required for the save to function $vardef['table'] = $this->getTablename($sourceModule); $vardef['module'] = $sourceModule; require_once 'modules/ModuleBuilder/parsers/relationships/AbstractRelationships.php'; $parsedModuleName = AbstractRelationships::parseDeployedModuleName($sourceModule); // now determine the appropriate 'rname' field for this relate // the 'rname' points to the field in source module that contains the displayable name for the record // usually this is 'name' but sometimes it is not... $vardef['rname'] = 'name'; if (isset($parsedModuleName['packageName'])) { require_once 'modules/ModuleBuilder/MB/ModuleBuilder.php'; $mb = new ModuleBuilder(); $module = $mb->getPackageModule($parsedModuleName['packageName'], $parsedModuleName['moduleName']); if (in_array('file', array_keys($module->config['templates']))) { $vardef['rname'] = 'document_name'; } } else { switch (strtolower($sourceModule)) { case 'prospects': $vardef['rname'] = 'account_name'; break; case 'documents': $vardef['rname'] = 'document_name'; break; case 'kbdocuments': $vardef['rname'] = 'kbdocument_name'; break; case 'leads': case 'contacts': // special handling as these modules lack a name column in the database; instead 'name' refers to a non-db field that concatenates first_name and last_name // luckily, the relate field mechanism can handle this with an equivalent additional db_concat_fields entry $vardef['rname'] = 'name'; $vardef['db_concat_fields'] = array(0 => 'first_name', 1 => 'last_name'); break; default: // now see if we have any module inheriting from the 'file' template - records in file-type modules are named by the document_name field, not the usual 'name' field $object = $GLOBALS['beanList'][$sourceModule]; require_once $GLOBALS['beanFiles'][$object]; $bean = new $object(); if (isset($GLOBALS['dictionary'][$object]['templates']) && in_array('file', $GLOBALS['dictionary'][$object]['templates'])) { $vardef['rname'] = 'document_name'; } } } return $vardef; }
function getProvidedSubpanels() { $this->providedSubpanels = array(); $subpanelDir = $this->getModuleDir() . '/metadata/subpanels/'; if (file_exists($subpanelDir)) { $f = dir($subpanelDir); require_once 'modules/ModuleBuilder/parsers/relationships/AbstractRelationships.php'; while ($g = $f->read()) { // sanity check to confirm that this is a usable subpanel... if (substr($g, 0, 1) != '.' && AbstractRelationships::validSubpanel($subpanelDir . $g)) { $subname = str_replace('.php', '', $g); $this->providedSubpanels[$subname] = $subname; } } } return $this->providedSubpanels; }
/** * gets a list of subpanels provided to other modules * * */ function getProvidedSubpanels() { require_once 'modules/ModuleBuilder/parsers/relationships/AbstractRelationships.php'; $this->providedSubpanels = array(); $subpanelDir = 'modules/' . $this->module . '/metadata/subpanels/'; foreach (array($subpanelDir, "custom/{$subpanelDir}") as $dir) { if (is_dir($dir)) { foreach (scandir($dir) as $fileName) { // sanity check to confirm that this is a usable subpanel... if (substr($fileName, 0, 1) != '.' && substr(strtolower($fileName), -4) == ".php" && AbstractRelationships::validSubpanel("{$dir}/{$fileName}")) { $subname = str_replace('.php', '', $fileName); $this->providedSubpanels[$subname] = $subname; } } } } return $this->providedSubpanels; }
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; }
protected function getRelateFieldDefinition($sourceModule, $relationshipName, $vnameLabel = '') { $vardef = array(); $vardef['name'] = $this->getValidDBName($relationshipName . "_name"); // must end in _name for the QuickSearch code in TemplateHandler->createQuickSearchCode $vardef['type'] = 'relate'; $vardef['source'] = 'non-db'; if (!empty($vnameLabel)) { $vardef['vname'] = 'LBL_' . strtoupper($relationshipName . '_FROM_' . $vnameLabel) . '_TITLE'; } else { $vardef['vname'] = 'LBL_' . strtoupper($relationshipName . '_FROM_' . $sourceModule) . '_TITLE'; } $vardef['save'] = true; // the magic value to tell SugarBean to save this relate field even though it is not listed in the $relationship_fields array // id_name matches the join_key_ column in the relationship table for the sourceModule - that is, the column in the relationship table containing the id of the corresponding field in the source module's table (vardef['table']) $vardef['id_name'] = $this->getIDName($sourceModule); // link cannot match id_name otherwise the $bean->$id_name value set from the POST is overwritten by the Link object created by this 'link' entry $vardef['link'] = $this->getValidDBName($relationshipName); // the name of the link field that points to the relationship - required for the save to function $vardef['table'] = $this->getTablename($sourceModule); $vardef['module'] = $sourceModule; $module = null; switch (strtolower($sourceModule)) { case 'prospects': $bean = BeanFactory::getBean($this->definition['rhs_module']); $fields = array_keys($bean->field_name_map); if (in_array('name', $fields)) { $vardef['rname'] = 'name'; } else { $vardef['rname'] = 'account_name'; } break; case 'documents': $vardef['rname'] = 'document_name'; break; case 'kbdocuments': $vardef['rname'] = 'kbdocument_name'; break; default: $module = $sourceModule; $vardef['rname'] = 'name'; break; } if ($module) { $class = BeanFactory::getBean($module); $tplconfig = array(); if (!$class) { $parsedModuleName = AbstractRelationships::parseDeployedModuleName($sourceModule); if (isset($parsedModuleName['packageName'])) { // added relationship to yet non-deployed module require_once 'modules/ModuleBuilder/MB/ModuleBuilder.php'; $mb = new ModuleBuilder(); $module = $mb->getPackageModule($parsedModuleName['packageName'], $parsedModuleName['moduleName']); $tplconfig = array_keys($module->config['templates']); } else { throw new \RuntimeException('Module does not exist as a bean and no template found in its config'); } } if (is_subclass_of($class, 'File') || in_array('file', $tplconfig)) { $vardef['rname'] = 'document_name'; } elseif (is_subclass_of($class, 'Person') || in_array('person', $tplconfig)) { $vardef['rname'] = 'full_name'; $vardef['db_concat_fields'] = array(0 => 'first_name', 1 => 'last_name'); } elseif ($class && $class->getFieldDefinition('name')) { $vardef['rname'] = 'name'; } } return $vardef; }
private function updateUndeployedLayout($relationship, $actionAdd = true) { // many-to-many relationships don't have fields so if we have a many-to-many we can just skip this... if ($relationship->getType() == MB_MANYTOMANY) { return false; } $successful = true; $layoutAdditions = $relationship->buildFieldsToLayouts(); require_once 'modules/ModuleBuilder/parsers/views/GridLayoutMetaDataParser.php'; foreach ($layoutAdditions as $deployedModuleName => $fieldName) { foreach (array(MB_EDITVIEW, MB_DETAILVIEW) as $view) { $parsedName = AbstractRelationships::parseDeployedModuleName($deployedModuleName); if (isset($parsedName['packageName'])) { $GLOBALS['log']->debug(get_class($this) . ": " . ($actionAdd ? "adding" : "removing") . " {$fieldName} on {$view} layout for undeployed module {$parsedName['moduleName']} in package {$parsedName['packageName']}"); $parser = new GridLayoutMetaDataParser($view, $parsedName['moduleName'], $parsedName['packageName']); if ($actionAdd ? $parser->addField(array('name' => $fieldName)) : $parser->removeField($fieldName)) { $parser->handleSave(false); } else { $GLOBALS['log']->debug(get_class($this) . ": couldn't " . ($actionAdd ? "add" : "remove") . " {$fieldName} on {$view} layout for undeployed module {$deployedModuleName}"); $successful = false; } } } } return $successful; }
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"); }