Add php files

This commit is contained in:
2025-05-12 15:44:39 +00:00
parent c951760058
commit 82d5804ac4
9534 changed files with 2638137 additions and 0 deletions

View File

@@ -0,0 +1,173 @@
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
class ModuleBuilderParser
{
var $_defMap; // private - mapping from view to variable name inside the viewdef file
var $_variables = array(); // private - set of additional variables (other than the viewdefs) found in the viewdef file that need to be added to the file again when it is saved - used by ModuleBuilder
function ModuleBuilderParser()
{
$this->_defMap = array('listview'=>'listViewDefs','searchview'=>'searchdefs','editview'=>'viewdefs','detailview'=>'viewdefs','quickcreate'=>'viewdefs');
}
/*
* Initialize this parser
*/
function init ()
{
}
/*
* Dummy function used to ease the transition to the new parser structure
*/
function populateFromPost()
{
}
function _loadFromFile($view,$file,$moduleName)
{
$variables = array();
if (! file_exists($file))
{
$this->_fatalError("ModuleBuilderParser: required viewdef file {$file} does not exist");
}
$GLOBALS['log']->info('ModuleBuilderParser->_loadFromFile(): file='.$file);
require ($file); // loads in a $viewdefs
// Check to see if we have the module name set as a variable rather than embedded in the $viewdef array
// If we do, then we have to preserve the module variable when we write the file back out
// This is a format used by ModuleBuilder templated modules to speed the renaming of modules
// Traditional Sugar modules don't use this format
// We must do this in ParserModifyLayout (rather than just in ParserBuildLayout) because we might be editing the layout of a MB created module in Studio after it has been deployed
$moduleVariables = array('module_name','_module_name', 'OBJECT_NAME', '_object_name');
foreach ($moduleVariables as $name)
{
if (isset($$name)) {
$variables[$name] = $$name;
}
}
$viewVariable = $this->_defMap[strtolower($view)];
// Now tidy up the module name in the viewdef array
// MB created definitions store the defs under packagename_modulename and later methods that expect to find them under modulename will fail
$defs = $$viewVariable;
if (isset($variables['module_name']))
{
$mbName = $variables['module_name'];
if ($mbName != $moduleName)
{
$GLOBALS['log']->debug('ModuleBuilderParser->_loadFromFile(): tidying module names from '.$mbName.' to '.$moduleName);
$defs[$moduleName] = $defs[$mbName];
unset($defs[$mbName]);
}
}
// $GLOBALS['log']->debug('ModuleBuilderParser->_loadFromFile(): '.print_r($defs,true));
return (array('viewdefs' => $defs, 'variables' => $variables));
}
function handleSave ($file,$view,$moduleName,$defs)
{
}
/*
* Save the new layout
*/
function _writeToFile ($file,$view,$moduleName,$defs,$variables)
{
mkdir_recursive(dirname($file));
$GLOBALS['log']->debug("ModuleBuilderParser->_writeFile(): file=".$file);
$useVariables = (count($variables)>0);
if( $fh = @sugar_fopen( $file, 'w' ) )
{
$out = "<?php\n";
if ($useVariables)
{
// write out the $<variable>=<modulename> lines
foreach($variables as $key=>$value)
{
$out .= "\$$key = '".$value."';\n";
}
}
// write out the defs array itself
switch (strtolower($view))
{
case 'editview':
case 'detailview':
case 'quickcreate':
$defs = array($view => $defs);
break;
default:
break;
}
$viewVariable = $this->_defMap[strtolower($view)];
$out .= "\$$viewVariable = ";
$out .= ($useVariables) ? "array (\n\$module_name =>\n".var_export_helper($defs) : var_export_helper( array($moduleName => $defs) );
// tidy up the parenthesis
if ($useVariables)
{
$out .= "\n)";
}
$out .= ";\n?>\n";
// $GLOBALS['log']->debug("parser.modifylayout.php->_writeFile(): out=".print_r($out,true));
fputs( $fh, $out);
fclose( $fh );
}
else
{
$GLOBALS['log']->fatal("ModuleBuilderParser->_writeFile() Could not write new viewdef file ".$file);
}
}
function _fatalError ($msg)
{
$GLOBALS ['log']->fatal($msg);
echo $msg;
sugar_cleanup();
die();
}
}
?>

View File

@@ -0,0 +1,122 @@
<?php
if (! defined ( 'sugarEntry' ) || ! sugarEntry)
die ( 'Not A Valid Entry Point' ) ;
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
require_once 'modules/ModuleBuilder/parsers/constants.php' ;
class ParserFactory
{
/*
* Create a new parser
*
* @param string $view The view, for example EditView or ListView. For search views, use advanced_search or basic_search
* @param string $moduleName Module name
* @param string $packageName Package name. If present implies that we are being called from ModuleBuilder
* @return AbstractMetaDataParser
*/
function getParser ( $view , $moduleName , $packageName = null , $subpanelName = null )
{
$GLOBALS [ 'log' ]->info ( "ParserFactory->getParser($view,$moduleName,$packageName,$subpanelName )" ) ;
if ( empty ( $packageName ) || ( $packageName == 'studio' ) )
$packageName = null ;
switch ( strtolower ( $view ))
{
case MB_EDITVIEW :
case MB_DETAILVIEW :
case MB_QUICKCREATE :
require_once 'modules/ModuleBuilder/parsers/views/GridLayoutMetaDataParser.php' ;
return new GridLayoutMetaDataParser ( $view, $moduleName, $packageName ) ;
case MB_CONVERTLEAD :
require_once 'modules/Leads/ConvertLayoutMetadataParser.php';
return new ConvertLayoutMetadataParser( $moduleName );
case MB_BASICSEARCH :
case MB_ADVANCEDSEARCH :
require_once 'modules/ModuleBuilder/parsers/views/SearchViewMetaDataParser.php' ;
return new SearchViewMetaDataParser ( $view, $moduleName, $packageName ) ;
case MB_LISTVIEW :
if ($subpanelName == null)
{
require_once 'modules/ModuleBuilder/parsers/views/ListLayoutMetaDataParser.php' ;
return new ListLayoutMetaDataParser ( MB_LISTVIEW, $moduleName, $packageName ) ;
} else
{
require_once 'modules/ModuleBuilder/parsers/views/SubpanelMetaDataParser.php' ;
return new SubpanelMetaDataParser ( $subpanelName, $moduleName, $packageName ) ;
}
case MB_DASHLET :
case MB_DASHLETSEARCH :
require_once 'modules/ModuleBuilder/parsers/views/DashletMetaDataParser.php' ;
return new DashletMetaDataParser($view, $moduleName, $packageName );
case MB_POPUPLIST :
case MB_POPUPSEARCH :
require_once 'modules/ModuleBuilder/parsers/views/PopupMetaDataParser.php' ;
return new PopupMetaDataParser($view, $moduleName, $packageName );
case MB_LABEL :
require_once 'modules/ModuleBuilder/parsers/parser.label.php' ;
return new ParserLabel ( $moduleName, $packageName ) ;
case MB_VISIBILITY :
require_once 'modules/ModuleBuilder/parsers/parser.visibility.php' ;
return new ParserVisibility ( $moduleName, $packageName ) ;
default :
$prefix = '';
if(!is_null ( $packageName )){
$prefix = empty($packageName) ? 'build' :'modify';
}
$fileName = "modules/ModuleBuilder/parsers/parser." . strtolower ( $prefix . $view ) . ".php" ;
if (file_exists ( $fileName ))
{
require_once $fileName ;
$class = 'Parser' . $prefix . ucfirst ( $view ) ;
if (class_exists ( $class ))
{
$GLOBALS [ 'log' ]->debug ( 'Using ModuleBuilder Parser ' . $fileName ) ;
$parser = new $class ( ) ;
return $parser ;
}
}
}
$GLOBALS [ 'log' ]->fatal ( get_class ( $this ) . ": cannot create ModuleBuilder Parser $fileName" ) ;
}
}
?>

View File

@@ -0,0 +1,158 @@
<?php
if (! defined ( 'sugarEntry' ) || ! sugarEntry)
die ( 'Not A Valid Entry Point' ) ;
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
require_once ('modules/DynamicFields/DynamicField.php') ;
class StandardField extends DynamicField
{
var $custom_def = array();
var $base_path = "";
var $baseField;
function __construct($module = '') {
$this->module = (! empty ( $module )) ? $module :( (isset($_REQUEST['module']) && ! empty($_REQUEST['module'])) ? $_REQUEST ['module'] : '');
$this->base_path = "custom/Extension/modules/{$this->module}/Ext/Vardefs";
}
protected function loadCustomDef($field){
global $beanList;
if (!empty($beanList[$this->module]) && is_file("custom/Extension/modules/{$this->module}/Ext/Vardefs/sugarfield_$field.php"))
{
$dictionary = array($beanList[$this->module] => array("fields" => array($field => array())));
include("$this->base_path/sugarfield_$field.php");
if (!empty($dictionary[$beanList[$this->module]]) && isset($dictionary[$beanList[$this->module]]["fields"][$field]))
$this->custom_def = $dictionary[$beanList[$this->module]]["fields"][$field];
}
}
/**
* Adds a custom field using a field object
*
* @param Field Object $field
* @return boolean
*/
function addFieldObject(&$field){
global $dictionary, $beanList;
if (empty($beanList[$this->module]))
return false;
$bean_name = $beanList[$this->module];
if (empty($dictionary[$bean_name]) || empty($dictionary[$bean_name]["fields"][$field->name]))
return false;
$currdef = $dictionary[$bean_name]["fields"][$field->name];
$this->loadCustomDef($field->name);
$newDef = $field->get_field_def();
require_once ('modules/DynamicFields/FieldCases.php') ;
$this->baseField = get_widget ( $field->type) ;
foreach ($field->vardef_map as $property => $fmd_col){
if ($property == "action" || $property == "label_value" || $property == "label"
|| ((substr($property, 0,3) == 'ext' && strlen($property) == 4))
)
continue;
// Bug 37043 - Avoid writing out vardef defintions that are the default value.
if (isset($newDef[$property]) &&
((!isset($currdef[$property]) && !$this->isDefaultValue($property,$newDef[$property], $this->baseField))
|| (isset($currdef[$property]) && $currdef[$property] != $newDef[$property])
)
){
$this->custom_def[$property] =
is_string($newDef[$property]) ? htmlspecialchars_decode($newDef[$property], ENT_QUOTES) : $newDef[$property];
}
}
if (isset($this->custom_def["duplicate_merge_dom_value"]) && !isset($this->custom_def["duplicate_merge"]))
unset($this->custom_def["duplicate_merge_dom_value"]);
$file_loc = "$this->base_path/sugarfield_{$field->name}.php";
$out = "<?php\n // created: " . date('Y-m-d H:i:s') . "\n";
foreach ($this->custom_def as $property => $val)
{
$out .= override_value_to_string_recursive(array($bean_name, "fields", $field->name, $property), "dictionary", $val) . "\n";
}
$out .= "\n ?>";
if (!file_exists($this->base_path))
mkdir_recursive($this->base_path);
if( $fh = @sugar_fopen( $file_loc, 'w' ) )
{
fputs( $fh, $out);
fclose( $fh );
return true ;
}
else
{
return false ;
}
}
protected function isDefaultValue($property, $value, $baseField)
{
switch ($property) {
case "importable":
return ( $value === 'true' || $value === '1' || $value === true || $value === 1 ); break;
case "required":
case "audited":
case "massupdate":
return ( $value === 'false' || $value === '0' || $value === false || $value === 0); break;
case "default_value":
case "default":
case "help":
case "comments":
return ($value == "");
case "duplicate_merge":
return ( $value === 'false' || $value === '0' || $value === false || $value === 0 || $value === "disabled"); break;
}
if (isset($baseField->$property))
{
return $baseField->$property == $value;
}
return false;
}
}
?>

View File

@@ -0,0 +1,69 @@
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
define('MB_BASEMETADATALOCATION','base');
define('MB_CUSTOMMETADATALOCATION','custom');
define('MB_WORKINGMETADATALOCATION','working');
define('MB_HISTORYMETADATALOCATION','history');
define('MB_GRIDLAYOUTMETADATA','gridLayoutMetaData');
define('MB_LISTLAYOUTMETADATA','listLayoutMetaData');
define('MB_LISTVIEW','listview');
define('MB_SEARCHVIEW','searchview');
define('MB_BASICSEARCH','basic_search' );
define('MB_ADVANCEDSEARCH','advanced_search' );
define('MB_DASHLET','dashlet');
define('MB_DASHLETSEARCH','dashletsearch');
define('MB_EDITVIEW','editview');
define('MB_DETAILVIEW','detailview');
define('MB_CONVERTLEAD','convertlead');
define('MB_QUICKCREATE','quickcreate');
define('MB_POPUPLIST','popuplist');
define('MB_POPUPSEARCH','popupsearch');
define('MB_LABEL','label');
define('MB_ONETOONE', 'one-to-one');
define('MB_ONETOMANY', 'one-to-many');
define('MB_MANYTOONE', 'many-to-one');
define('MB_MANYTOMANY', 'many-to-many');
define('MB_MAXDBIDENTIFIERLENGTH',30); // maximum length of any identifier in our supported databases
define('MB_EXPORTPREPEND','project_');
define('MB_VISIBILITY','visibility');
class MBConstants
{
static $EMPTY = array ( 'name' => '(empty)' , 'label' => '(empty)' ) ;
static $FILLER = array ( 'name' => '(filler)' , 'label' => 'LBL_FILLER' ) ; // would prefer to have label => translate('LBL_FILLER') but can't be done in a static, and don't want to require instantiating a new object to get these constants
}

View File

@@ -0,0 +1,210 @@
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
require_once('modules/ModuleBuilder/parsers/ModuleBuilderParser.php');
class ParserDropDown extends ModuleBuilderParser {
/**
* Takes in the request params from a save request and processes
* them for the save.
*
* @param REQUEST params $params
*/
function saveDropDown($params){
require_once('modules/Administration/Common.php');
$emptyMarker = translate('LBL_BLANK');
$selected_lang = (!empty($params['dropdown_lang'])?$params['dropdown_lang']:$_SESSION['authenticated_user_language']);
$type = $_REQUEST['view_package'];
$dir = '';
$dropdown_name = $params['dropdown_name'];
$json = getJSONobj();
$list_value = str_replace('&quot;&quot;:&quot;&quot;', '&quot;__empty__&quot;:&quot;&quot;', $params['list_value']);
//Bug 21362 ENT_QUOTES- convert single quotes to escaped single quotes.
$temp = $json->decode(html_entity_decode(rawurldecode($list_value), ENT_QUOTES) );
$dropdown = array () ;
// dropdown is received as an array of (name,value) pairs - now extract to name=>value format preserving order
// we rely here on PHP to preserve the order of the received name=>value pairs - associative arrays in PHP are ordered
foreach ( $temp as $item )
{
$dropdown[ $item [0] ] = $item [ 1 ] ;
}
if(array_key_exists($emptyMarker, $dropdown)){
$output=array();
foreach($dropdown as $key => $value){
if($emptyMarker===$key)
$output['']='';
else
$output[$key]=$value;
}
$dropdown=$output;
}
if($type != 'studio'){
$mb = new ModuleBuilder();
$module =& $mb->getPackageModule($params['view_package'], $params['view_module']);
$this->synchMBDropDown($dropdown_name, $dropdown, $selected_lang, $module);
//Can't use synch on selected lang as we want to overwrite values, not just keys
$module->mblanguage->appListStrings[$selected_lang.'.lang.php'][$dropdown_name] = $dropdown;
$module->mblanguage->save($module->key_name); // tyoung - key is required parameter as of
}else{
$contents = return_custom_app_list_strings_file_contents($selected_lang);
$my_list_strings = return_app_list_strings_language($selected_lang);
if($selected_lang == $GLOBALS['current_language']){
$GLOBALS['app_list_strings'][$dropdown_name] = $dropdown;
}
//write to contents
$contents = str_replace("?>", '', $contents);
if(empty($contents))$contents = "<?php";
//add new drop down to the bottom
if(!empty($params['use_push'])){
//this is for handling moduleList and such where nothing should be deleted or anything but they can be renamed
foreach($dropdown as $key=>$value){
//only if the value has changed or does not exist do we want to add it this way
if(!isset($my_list_strings[$dropdown_name][$key]) || strcmp($my_list_strings[$dropdown_name][$key], $value) != 0 ){
//clear out the old value
$pattern_match = '/\s*\$app_list_strings\s*\[\s*\''.$dropdown_name.'\'\s*\]\[\s*\''.$key.'\'\s*\]\s*=\s*[\'\"]{1}.*?[\'\"]{1};\s*/ism';
$contents = preg_replace($pattern_match, "\n", $contents);
//add the new ones
$contents .= "\n\$GLOBALS['app_list_strings']['$dropdown_name']['$key']=" . var_export_helper($value) . ";";
}
}
}else{
//Now synch up the keys in other langauges to ensure that removed/added Drop down values work properly under all langs.
$this->synchDropDown($dropdown_name, $dropdown, $selected_lang, $dir);
$contents = $this->getNewCustomContents($dropdown_name, $dropdown, $selected_lang);
}
if(!empty($dir) && !is_dir($dir))
{
$continue = mkdir_recursive($dir);
}
save_custom_app_list_strings_contents($contents, $selected_lang, $dir);
}
sugar_cache_reset();
clearAllJsAndJsLangFilesWithoutOutput();
}
/**
* function synchDropDown
* Ensures that the set of dropdown keys is consistant accross all languages.
*
* @param $dropdown_name The name of the dropdown to be synched
* @param $dropdown array The dropdown currently being saved
* @param $selected_lang String the language currently selected in Studio/MB
* @param $saveLov String the path to the directory to save the new lang file in.
*/
function synchDropDown($dropdown_name, $dropdown, $selected_lang, $saveLoc) {
$allLanguages = get_languages();
foreach ($allLanguages as $lang => $langName) {
if ($lang != $selected_lang) {
$listStrings = return_app_list_strings_language($lang);
$langDropDown = array();
if (isset($listStrings[$dropdown_name]) && is_array($listStrings[$dropdown_name]))
{
$langDropDown = $this->synchDDKeys($dropdown, $listStrings[$dropdown_name]);
} else
{
//if the dropdown does not exist in the language, justt use what we have.
$langDropDown = $dropdown;
}
$contents = $this->getNewCustomContents($dropdown_name, $langDropDown, $lang);
save_custom_app_list_strings_contents($contents, $lang, $saveLoc);
}
}
}
/**
* function synchMBDropDown
* Ensures that the set of dropdown keys is consistant accross all languages in a ModuleBuilder Module
*
* @param $dropdown_name The name of the dropdown to be synched
* @param $dropdown array The dropdown currently being saved
* @param $selected_lang String the language currently selected in Studio/MB
* @param $module MBModule the module to update the languages in
*/
function synchMBDropDown($dropdown_name, $dropdown, $selected_lang, $module) {
$selected_lang = $selected_lang . '.lang.php';
foreach($module->mblanguage->appListStrings as $lang => $listStrings) {
if ($lang != $selected_lang)
{
$langDropDown = array();
if (isset($listStrings[$dropdown_name]) && is_array($listStrings[$dropdown_name]))
{
$langDropDown = $this->synchDDKeys($dropdown, $listStrings[$dropdown_name]);
} else
{
$langDropDown = $dropdown;
}
$module->mblanguage->appListStrings[$lang][$dropdown_name] = $langDropDown;
$module->mblanguage->save($module->key_name);
}
}
}
private function synchDDKeys($dom, $sub) {
//check for extra keys
foreach($sub as $key=>$value) {
if (!isset($dom[$key])) {
unset ($sub[$key]);
}
}
//check for missing keys
foreach($dom as $key=>$value) {
if (!isset($sub[$key])) {
$sub[$key] = $value;
}
}
return $sub;
}
function getPatternMatch($dropdown_name) {
return '/\s*\$GLOBALS\s*\[\s*\'app_list_strings\s*\'\s*\]\[\s*\''
. $dropdown_name.'\'\s*\]\s*=\s*array\s*\([^\)]*\)\s*;\s*/ism';
}
function getNewCustomContents($dropdown_name, $dropdown, $lang) {
$contents = return_custom_app_list_strings_file_contents($lang);
$contents = str_replace("?>", '', $contents);
if(empty($contents))$contents = "<?php";
$contents = preg_replace($this->getPatternMatch($dropdown_name), "\n", $contents);
$contents .= "\n\$GLOBALS['app_list_strings']['$dropdown_name']=" . var_export_helper($dropdown) . ";";
return $contents;
}
}
?>

View File

@@ -0,0 +1,189 @@
<?php
if (! defined ( 'sugarEntry' ) || ! sugarEntry)
die ( 'Not A Valid Entry Point' ) ;
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
require_once ('modules/ModuleBuilder/parsers/ModuleBuilderParser.php') ;
class ParserLabel extends ModuleBuilderParser
{
function ParserLabel ($moduleName, $packageName = '' )
{
$this->moduleName = $moduleName;
if (!empty($packageName))
$this->packageName = $packageName ;
}
/**
* Takes in the request params from a save request and processes
* them for the save.
* @param REQUEST $params Labels as "label_".System label => Display label pairs
* @param string $language Language key, for example 'en_us'
*/
function handleSave ($params , $language)
{
$labels = array ( ) ;
foreach ( $params as $key => $value )
{
if (preg_match ( '/^label_/', $key ) && strcmp ( $value, 'no_change' ) != 0)
{
$labels [ strtoupper(substr ( $key, 6 )) ] = $value ;
}
}
if (!empty($this->packageName)) //we are in Module builder
{
return self::addLabels ( $language, $labels, $this->moduleName, "custom/modulebuilder/packages/{$this->packageName}/modules/{$this->moduleName}/language" ) ;
} else
{
return self::addLabels ( $language, $labels, $this->moduleName ) ;
}
}
/*
* Add a set of labels to the language pack for a module, deployed or undeployed
* @param string $language Language key, for example 'en_us'
* @param array $labels The labels to add in the form of an array of System label => Display label pairs
* @param string $moduleName Name of the module to which to add these labels
* @param string $packageName If module is undeployed, name of the package to which it belongs
*/
static function addLabels ($language , $labels , $moduleName , $basepath = null, $forRelationshipLabel = false)
{
$GLOBALS [ 'log' ]->debug ( "ParserLabel->addLabels($language, \$labels, $moduleName, $basepath );" ) ;
$GLOBALS [ 'log' ]->debug ( "\$labels:" . print_r ( $labels, true ) ) ;
$deployedModule = false ;
if (is_null ( $basepath ))
{
$deployedModule = true ;
$basepath = "custom/modules/$moduleName/language" ;
if($forRelationshipLabel){
$basepath = "custom/modules/$moduleName/Ext/Language" ;
}
if (! is_dir ( $basepath ))
{
mkdir_recursive($basepath);
}
}
$filename = "$basepath/$language.lang.php" ;
if($forRelationshipLabel){
$filename = "$basepath/$language.lang.ext.php" ;
}
$dir_exists = is_dir ( $basepath ) ;
$mod_strings = array ( ) ;
if ($dir_exists)
{
if (file_exists ( $filename ))
{
// obtain $mod_strings
include ($filename) ;
}else if($forRelationshipLabel){
$fh = fopen ($filename, 'a');
fclose($fh);
}
} else
{
return false ;
}
$changed = false ;
//$charset = (isset($app_strings['LBL_CHARSET'])) ? $app_strings['LBL_CHARSET'] : $GLOBALS['sugar_config']['default_charset'] ;
foreach ( $labels as $key => $value )
{
if (! isset ( $mod_strings [ $key ] ) || strcmp ( $value, $mod_strings [ $key ] ) != 0)
{
$mod_strings [$key] = html_entity_decode_utf8($value, ENT_QUOTES ); // must match encoding used in view.labels.php
$changed = true ;
}
}
if ($changed)
{
$GLOBALS [ 'log' ]->debug ( "ParserLabel->addLabels: writing new mod_strings to $filename" ) ;
$GLOBALS [ 'log' ]->debug ( "ParserLabel->addLabels: mod_strings=".print_r($mod_strings,true) ) ;
if (! write_array_to_file ( "mod_strings", $mod_strings, $filename ))
{
$GLOBALS [ 'log' ]->fatal ( "Could not write $filename" ) ;
} else
{
// if we have a cache to worry about, then clear it now
if ($deployedModule)
{
$GLOBALS [ 'log' ]->debug ( "PaserLabel->addLabels: clearing language cache" ) ;
$cache_key = "module_language." . $language . $moduleName ;
sugar_cache_clear ( $cache_key ) ;
LanguageManager::clearLanguageCache ( $moduleName, $language ) ;
}
}
}
return true ;
}
/**
* Takes in the request params from a save request and processes
* them for the save.
* @param $metadata
* @param string $language Language key, for example 'en_us'
*/
function handleSaveRelationshipLabels ($metadata , $language)
{
foreach ( $metadata as $definition )
{
$labels = array();
$labels[$definition [ 'system_label' ]] = $definition [ 'display_label' ];
self::addLabels ( $language, $labels, $definition [ 'module' ],null,true );
}
}
function addLabelsToAllLanguages($labels)
{
$langs = get_languages();
foreach($langs as $lang_key => $lang_display)
{
$this->addLabels($lang_key, $labels, $this->moduleName);
}
}
}
?>

View File

@@ -0,0 +1,524 @@
<?php
if (! defined('sugarEntry') || ! sugarEntry)
die('Not A Valid Entry Point');
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
require_once ('modules/ModuleBuilder/parsers/ModuleBuilderParser.php');
class ParserModifyLayoutView extends ModuleBuilderParser
{
var $maxColumns; // number of columns in this layout
var $usingWorkingFile = false; // if a working file exists (used by view.edit.php among others to determine the title for the layout edit panel)
var $language_module; // set to module name for studio, passed to the smarty template and used by sugar_translate
var $_sourceFile; // private - the source of the layout defn
var $_customFile; // private
var $_workingFile; // private
var $_originalFile; //private
var $_moduleVariable; // private - if set, contains the name of the variable containing the module name in the $viewdef file
var $_module; // private
var $_view; // private
var $_viewdefs; // private
var $_fieldDefs; // private
/**
* Constructor
*/
function init ($module, $view, $submittedLayout = false)
{
$this->_view = ucfirst($view);
$this->_module = $module;
$this->language_module = $module;
$this->_baseDirectory = "modules/{$module}/metadata/";
$file = $this->_baseDirectory . strtolower($view) . "defs.php";
$this->_customFile = "custom/" . $file;
$this->_workingFile = "custom/working/" . $file;
$this->_sourceView = $this->_view;
$this->_originalFile = $file ;
$this->_sourceFile = $file;
if (is_file($this->_workingFile))
{
$this->_sourceFile = $this->_workingFile;
$this->usingWorkingFile = true;
}
else if (is_file($this->_customFile))
{
$this->_sourceFile = $this->_customFile;
}
else if (! is_file($this->_sourceFile))
{
// if we don't have ANY defined metadata then improvise as best we can
if (strtolower($this->_view) == 'quickcreate')
{
// special handling for quickcreates - base the quickcreate on the editview if no quickcreatedef exists
$this->_sourceFile = $this->_baseDirectory."editviewdefs.php";
if (is_file("custom/" . $this->_sourceFile))
{
$this->_sourceFile = "custom/" . $this->_sourceFile;
}
$this->_sourceView = 'EditView';
}
else
{
$this->_fatalError('parser.modifylayout.php->init(): no metadata for '.$this->_module.' '.$this->_view);
}
}
// get the fieldDefs from the bean
$class = $GLOBALS ['beanList'] [$module];
require_once ($GLOBALS ['beanFiles'] [$class]);
$bean = new $class();
$this->_fieldDefs = & $bean->field_defs;
$this->loadModule($this->_module, $this->_sourceView);
$this->_viewdefs ['panels'] = $this->_parseData($this->_viewdefs['panels']); // put into a canonical format
$this->maxColumns = $this->_viewdefs ['templateMeta'] ['maxColumns'];
if ($submittedLayout)
{
// replace the definitions with the new submitted layout
$this->_loadLayoutFromRequest();
} else
{
$this->_padFields(); // destined for a View, so we want to add in (empty) fields
}
// $GLOBALS['log']->debug($this->_viewdefs['panels']);
}
function getAvailableFields ()
{
// Available fields are those that are in the Model and the original layout definition, but not already shown in the View
// So, because the formats of the two are different we brute force loop through View and unset the fields we find in a copy of Model
$availableFields = $this->_getModelFields();
$GLOBALS['log']->debug( get_class($this)."->getAvailableFields(): _getModelFields returns: ".implode(",",array_keys($availableFields)));
if (! empty($this->_viewdefs))
{
foreach ($this->_viewdefs ['panels'] as $panel)
{
foreach ($panel as $row)
{
foreach ($row as $fieldArray)
{ // fieldArray is an array('name'=>name,'label'=>label)
if (isset($fieldArray ['name']))
{
unset($availableFields [$fieldArray ['name']]);
$GLOBALS['log']->debug( get_class($this)."->getAvailableFields(): removing ".$fieldArray ['name'] );
}
}
}
}
}
return $availableFields;
}
function getLayout ()
{
return $this->_viewdefs ['panels'];
}
function writeWorkingFile ()
{
$this->_writeToFile($this->_workingFile,$this->_view,$this->_module,$this->_viewdefs,$this->_variables);
}
function handleSave ()
{
$this->_writeToFile($this->_customFile,$this->_view,$this->_module,$this->_viewdefs,$this->_variables);
// now clear the cache so that the results are immediately visible
include_once('include/TemplateHandler/TemplateHandler.php');
if (strtolower($this->_view) == 'quickcreate')
{
TemplateHandler::clearCache($this->_module,"form_SubPanelQuickCreate_{$this->_module}.tpl");
TemplateHandler::clearCache($this->_module,"form_DCQuickCreate_{$this->_module}.tpl");
}
else
{
TemplateHandler::clearCache($this->_module,"{$this->_view}.tpl");
}
}
function loadModule ($module, $view)
{
$this->_viewdefs = array();
$viewdefs = null;
$loaded = $this->_loadFromFile($view,$this->_sourceFile,$module);
$this->_viewdefs = $loaded['viewdefs'][$module][$view];
$this->_variables = $loaded['variables'];
}
/**
* Load the canonical panel layout from the submitted form
*
*/
function _loadLayoutFromRequest ()
{
$i = 1;
// set up the map of panel# (as provided in the _REQUEST) to panel ID (as used in $this->_viewdefs['panels'])
foreach ($this->_viewdefs ['panels'] as $panelID => $panel)
{
$panelMap [$i ++] = $panelID;
}
// replace any old values with new panel labels from the request
foreach ($_REQUEST as $key => $value)
{
$components = explode('-', $key);
if ($components [0] == 'panel')
{
$panelMap [$components ['1']] = $value;
}
}
$olddefs = $this->_viewdefs ['panels'];
$origFieldDefs = $this->_getOrigFieldViewDefs();
// $GLOBALS['log']->debug('origFieldDefs');
// $GLOBALS['log']->debug($origFieldDefs);
$this->_viewdefs ['panels'] = null; // because the new field properties should replace the old fields, not be merged
if ($this->maxColumns < 1)
{
$this->_fatalError("EditDetailViewParser:invalid maxColumns=" . $this->maxColumns);
}
foreach ($_REQUEST as $slot => $value)
{
$slotComponents = explode('-', $slot); // [0] = 'slot', [1] = panel #, [2] = slot #, [3] = property name
if ($slotComponents [0] == 'slot')
{
$slotNumber = $slotComponents ['2'];
$panelID = $panelMap [$slotComponents ['1']];
$rowID = floor($slotNumber / $this->maxColumns);
$colID = $slotNumber - ($rowID * $this->maxColumns);
//If the original editview defined this field, copy that over.
if ($slotComponents ['3'] == 'name' && isset($origFieldDefs [$value]) && is_array($origFieldDefs [$value]))
{
$this->_viewdefs ['panels'] [$panelID] [$rowID] [$colID] = $origFieldDefs [$value];
}
else
{
$property = $slotComponents ['3'];
if ($value == '(filler)')
{
$this->_viewdefs ['panels'] [$panelID] [$rowID] [$colID] = NULL;
} else
{
$this->_viewdefs ['panels'] [$panelID] [$rowID] [$colID] [$property] = $value;
}
}
}
}
// Now handle the (empty) fields - first non-(empty) field goes in at column 0; all other (empty)'s removed
// Do this AFTER reading in all the $_REQUEST parameters as can't guarantee the order of those, and we need to operate on complete rows
foreach ($this->_viewdefs ['panels'] as $panelID => $panel)
{
// remove all (empty)s
foreach ($panel as $rowID => $row)
{
$startOfRow = true;
$offset = 0;
foreach ($row as $colID => $col)
{
if ($col ['name'] == '(empty)')
{
// if a leading (empty) then remove (by noting that remaining fields need to be shuffled along)
if ($startOfRow)
{
$offset ++;
}
unset($row [$colID]);
} else
{
$startOfRow = false;
}
}
// reindex to remove leading (empty)s
$newRow = array();
foreach ($row as $colID => $col)
{
$newRow [$colID - $offset] = $col;
}
$this->_viewdefs ['panels'] [$panelID] [$rowID] = $newRow;
}
}
// _pp($this->_viewdefs);
}
function _padFields ()
{
if (! empty($this->_viewdefs))
{
foreach ($this->_viewdefs ['panels'] as $panelID => $panel)
{
$column = 0;
foreach ($panel as $rowID => $row)
{
// pad between fields on a row
foreach ($row as $colID => $col)
{
for ($i = $column + 1 ; $i < $colID ; $i ++ )
{
$row [$i] = array('name' => '(empty)', 'label' => '(empty)');
}
$column = $colID;
}
// now pad out to the end of the row
if (($column + 1) < $this->maxColumns)
{ // last column is maxColumns-1
for ($i = $column + 1 ; $i < $this->maxColumns ; $i ++ )
{
$row [$i] = array('name' => '(empty)', 'label' => '(empty)');
}
}
ksort($row);
$this->_viewdefs ['panels'] [$panelID] [$rowID] = $row;
}
}
}
}
// add a new field to the end of a panel
// don't write out (caller should call handleSave() when ready)
function _addField ($properties, $panelID = FALSE)
{
// if a panelID was not passed, use the first available panel in the list
if (!$panelID) {
$panels = array_keys($this->_viewdefs['panels']);
$panelID = $panels[0];
}
if (isset($this->_viewdefs ['panels'] [$panelID]))
{
// need to clean up the viewdefs before writing them -- Smarty will fail if any fillers/empties are present
foreach ($this->_viewdefs['panels'] as $loop_panelID => $panel_contents) {
foreach ($panel_contents as $row_id => $row) {
foreach ($row as $col_id => $col) {
if ($col['name'] == '(filler)') {
$this->_viewdefs['panels'][$loop_panelID][$row_id][$col_id] = NULL;
}
elseif ($col['name'] == '(empty)') {
unset($this->_viewdefs['panels'][$loop_panelID][$row_id][$col_id]);
}
}
}
}
$panel = $this->_viewdefs ['panels'] [$panelID];
$lastrow = count($panel) - 1; // index starts at 0
$lastcol = count($panel [$lastrow]);
// if we're on the last column of the last row, start a new row
// print "lastrow=$lastrow lastcol=$lastcol";
if ($lastcol >= $this->maxColumns)
{
$lastrow ++;
$this->_viewdefs ['panels'] [$panelID] [$lastrow] = array();
$lastcol = 0;
}
$this->_viewdefs ['panels'] [$panelID] [$lastrow] [$lastcol] = $properties;
}
}
/* getModelFields returns an array of all fields stored in the database for this module plus those fields in the original layout definition (so we get fields such as Team ID)*/
function _getModelFields ()
{
$modelFields = array();
$origViewDefs = $this->_getOrigFieldViewDefs();
// $GLOBALS['log']->debug("Original viewdefs = ".print_r($origViewDefs,true));
foreach ($origViewDefs as $field => $def)
{
if (!empty($field))
{
if (! is_array($def)) {
$def = array('name' => $field);
}
// get this field's label - if it has not been explicitly provided, see if the fieldDefs has a label for this field, and if not fallback to the field name
if (! isset($def ['label']))
{
if (! empty($this->_fieldDefs [$field] ['vname']))
{
$def ['label'] = $this->_fieldDefs [$field] ['vname'];
} else
{
$def ['label'] = $field;
}
}
$modelFields[$field] = array('name' => $field, 'label' => $def ['label']);
}
}
$GLOBALS['log']->debug(print_r($modelFields,true));
foreach ($this->_fieldDefs as $field => $def)
{
if ((!empty($def['studio']) && $def['studio'] == 'visible')
|| (empty($def['studio']) && (empty($def ['source']) || $def ['source'] == 'db' || $def ['source'] == 'custom_fields') && $def ['type'] != 'id' && strcmp($field, 'deleted') != 0 && (empty($def ['dbType']) || $def ['dbType'] != 'id') && (empty($def ['dbtype']) || $def ['dbtype'] != 'id')))
{
$label = isset($def['vname']) ? $def['vname'] : $def['name'];
$modelFields [$field] = array('name' => $field, 'label' => $label);
}
else
{
$GLOBALS['log']->debug( get_class($this)."->_getModelFields(): skipping $field from modelFields as it fails the test for inclusion");
if ($field == 'assigned_user_name')
{
$GLOBALS['log']->debug( print_r($def,true));
}
}
}
$GLOBALS['log']->debug( get_class($this)."->_getModelFields(): remaining entries in modelFields are: ".implode(",",array_keys($modelFields)));
return $modelFields;
}
function _parseData ($panels)
{
$fields = array();
if (empty($panels))
return;
// Fix for a flexibility in the format of the panel sections - if only one panel, then we don't have a panel level defined, it goes straight into rows
// See EditView2 for similar treatment
if (! empty($panels) && count($panels) > 0)
{
$keys = array_keys($panels);
if (is_numeric($keys [0]))
{
$defaultPanel = $panels;
unset($panels); //blow away current value
$panels [''] = $defaultPanel;
}
}
foreach ($panels as $panelID => $panel)
{
foreach ($panel as $rowID => $row)
{
foreach ($row as $colID => $col)
{
$properties = array();
if (! empty($col))
{
if (is_string($col))
{
$properties ['name'] = $col;
} else if (! empty($col ['name']))
{
$properties = $col;
}
} else
{
$properties ['name'] = translate('LBL_FILLER');
}
if (! empty($properties ['name']))
{
// get this field's label - if it has not been explicity provided, see if the fieldDefs has a label for this field, and if not fallback to the field name
if (! isset($properties ['label']))
{
if (! empty($this->_fieldDefs [$properties ['name']] ['vname']))
{
$properties ['label'] = $this->_fieldDefs [$properties ['name']] ['vname'];
} else
{
$properties ['label'] = $properties ['name'];
}
}
$displayData[strtoupper($panelID)] [$rowID] [$colID] = $properties;
}
}
}
}
return $displayData;
}
function _getOrigFieldViewDefs ()
{
$origFieldDefs = array();
$GLOBALS['log']->debug("Original File = ".$this->_originalFile);
if (file_exists($this->_originalFile))
{
include ($this->_originalFile);
$origdefs = $viewdefs [$this->_module] [$this->_sourceView] ['panels'];
// $GLOBALS['log']->debug($origdefs);
// Fix for a flexibility in the format of the panel sections - if only one panel, then we don't have a panel level defined, it goes straight into rows
// See EditView2 for similar treatment
if (! empty($origdefs) && count($origdefs) > 0)
{
$keys = array_keys($origdefs);
if (is_numeric($keys [0]))
{
$defaultPanel = $origdefs;
unset($origdefs); //blow away current value
$origdefs [''] = $defaultPanel;
}
}
foreach ($origdefs as $pname => $paneldef)
{
foreach ($paneldef as $row)
{
foreach ($row as $fieldDef)
{
if (is_array($fieldDef))
{
$fieldName = $fieldDef ['name'];
}
else
{
$fieldName = $fieldDef;
}
$origFieldDefs [$fieldName] = $fieldDef;
}
}
}
}
return $origFieldDefs;
}
}
?>

View File

@@ -0,0 +1,324 @@
<?php
if (! defined ( 'sugarEntry' ) || ! sugarEntry)
die ( 'Not A Valid Entry Point' ) ;
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
require_once ('modules/ModuleBuilder/parsers/ModuleBuilderParser.php') ;
class ParserModifyListView extends ModuleBuilderParser
{
var $listViewDefs = false ;
var $defaults = array ( ) ;
var $additional = array ( ) ;
var $available = array ( ) ;
var $reserved = array(); // fields marked by 'studio'=>false in the listviewdefs; need to be preserved
// var $language_module = '';
var $columns = array ( 'LBL_DEFAULT' => 'getDefaultFields' , 'LBL_AVAILABLE' => 'getAdditionalFields' , 'LBL_HIDDEN' => 'getAvailableFields' ) ;
function init ( $module_name , $submodule = '' )
{
global $app_list_strings ;
$this->module_name = $module_name ;
$mod_strings = return_module_language ( $GLOBALS [ 'current_language' ], $this->module_name ) ; // needed solely so that listviewdefs that reference this can be included without error
$class = $GLOBALS [ 'beanList' ] [ $this->module_name ] ;
require_once ($GLOBALS [ 'beanFiles' ] [ $class ]) ;
$this->module = new $class ( ) ;
$loaded = $this->_loadFromFile('ListView','modules/' . $this->module_name . '/metadata/listviewdefs.php',$this->module_name);
$this->originalListViewDefs = $loaded['viewdefs'] [ $this->module_name ] ;
$this->_variables = $loaded['variables'];
// _pp($loaded);
$this->customFile = 'custom/modules/' . $this->module_name . '/metadata/listviewdefs.php' ;
if (file_exists ( $this->customFile ))
{
$loaded = $this->_loadFromFile('ListView',$this->customFile,$this->module_name);
$this->listViewDefs = $loaded['viewdefs'] [ $this->module_name ] ;
$this->_variables = $loaded['variables'];
} else
{
$this->listViewDefs = & $this->originalListViewDefs ;
}
$this->fixKeys ( $this->originalListViewDefs ) ;
$this->fixKeys ( $this->listViewDefs ) ;
$this->language_module = $this->module_name ;
}
function getLanguage()
{
return $this->language_module;
}
// re-key array so that every entry has a key=name and all keys are lowercase - makes it easier in handleSave() later...
function fixKeys ( &$defs )
{
$temp = array ( ) ;
foreach ( $defs as $key => $value )
{
if (! is_array ( $value ))
{
$key = $value ;
$def = array ( ) ;
$def [ 'name' ] = (isset ( $this->module->field_defs [ $key ] )) ? $this->module->field_defs [ $key ] [ 'name' ] : $key ;
$value = $def ;
}
if (isset ( $value [ 'name' ] ))
{
$key = $value [ 'name' ] ; // override key with name, needed when the entry lacks a key
}
$temp [ strtolower ( $key ) ] = $value ;
}
$defs = $temp ;
}
/**
* returns the default fields for a listview
* Called only when displaying the listview for editing; not called when saving
*/
function getDefaultFields ()
{
$this->defaults = array ( ) ;
foreach ( $this->listViewDefs as $key => $def )
{
// add in the default fields from the listviewdefs, stripping out any field with 'studio' set to a value other than true
// Important: the 'studio' fields must be added back into the layout on save, as they're not editable rather than hidden
if (! empty ( $def [ 'default' ] ))
{
if (! isset($def['studio']) || $def['studio'] === true)
{
$this->defaults [ $key ] = $def ;
}
else
// anything which doesn't go into the defaults is a reserved field - this makes sure we don't miss anything
{
$this->reserved [ $key ] = $def;
}
}
}
return $this->defaults ;
}
/**
* returns additional fields available for users to create fields
*/
function getAdditionalFields ()
{
$this->additional = array ( ) ;
foreach ( $this->listViewDefs as $key => $def )
{
if (empty ( $def [ 'default' ] ))
{
$key = strtolower ( $key ) ;
$this->additional [ $key ] = $def ;
}
}
return $this->additional ;
}
/**
* returns unused fields that are available for using in either default or additional list views
*/
function getAvailableFields ()
{
$this->availableFields = array ( ) ;
$lowerFieldList = array_change_key_case ( $this->listViewDefs ) ;
foreach ( $this->originalListViewDefs as $key => $def )
{
$key = strtolower ( $key ) ;
if (! isset ( $lowerFieldList [ $key ] ))
{
$this->availableFields [ $key ] = $def ;
}
}
$GLOBALS['log']->debug('parser.modifylistview.php->getAvailableFields(): field_defs='.print_r($this->availableFields,true));
$modFields = !empty($this->module->field_name_map) ? $this->module->field_name_map : $this->module->field_defs;
foreach ( $modFields as $key => $def )
{
$fieldName = strtolower ( $key ) ;
if ($fieldName == 'currency_id')
continue;
if (!isset ( $lowerFieldList [ $fieldName ] )) // bug 16728 - check this first, so that other conditions (e.g., studio == visible) can't override and add duplicate entries
{
// bug 19656: this test changed after 5.0.0b - we now remove all ID type fields - whether set as type, or dbtype, from the fielddefs
if ($this->isValidField($key, $def)){
$label = (isset ( $def [ 'vname' ] )) ? $def [ 'vname' ] : (isset($def [ 'label' ]) ? $def['label'] : $def['name']) ;
$this->availableFields [ $fieldName ] = array ( 'width' => '10' , 'label' => $label ) ;
}
}
}
return $this->availableFields ;
}
function isValidField($key, $def) {
//Allow fields that are studio visible
if (! empty ( $def [ 'studio' ] ) && $def [ 'studio' ] == 'visible')
return true;
//No ID fields
if ((!empty ( $def [ 'dbType' ] ) && $def [ 'dbType' ] == 'id') || (!empty ( $def [ 'type' ] ) && $def [ 'type' ] == 'id'))
return false;
//only allow DB and custom fields (if a source is specified)
if (!empty($def [ 'source' ]) && $def [ 'source' ] != 'db' && $def [ 'source' ] != 'custom_fields')
return false;
//Dont ever show the "deleted" fields or "_name" fields
if (strcmp ( $key, 'deleted' ) == 0 || (isset ( $def [ 'name' ] ) && strpos ( $def [ 'name' ], "_name" ) !== false))
return false;
_pp($def);
//If none of the "ifs" are true, the field is valid
return true;
}
function getField ( $fieldName )
{
$fieldName = strtolower ( $fieldName ) ;
foreach ( $this->listViewDefs as $key => $def )
{
$key = strtolower ( $key ) ;
if ($key == $fieldName)
{
return $def ;
}
}
foreach ( $this->module->field_defs as $key => $def )
{
$key = strtolower ( $key ) ;
if ($key == $fieldName)
{
return $def ;
}
}
return array ( ) ;
}
function addRelateData($fieldname, $listfielddef) {
$modFieldDef = $this->module->field_defs [ strtolower ( $fieldname ) ];
if (!empty($modFieldDef['module']) && !empty($modFieldDef['id_name'])) {
$listfielddef['module'] = $modFieldDef['module'];
$listfielddef['id'] = strtoupper($modFieldDef['id_name']);
$listfielddef['link'] = true;
$listfielddef['related_fields'] = array (strtolower($modFieldDef['id_name']));
}
return $listfielddef;
}
function _loadLayoutFromRequest ()
{
$GLOBALS['log']->debug("ParserModifyListView->_loadLayoutFromRequest()");
$fields = array ( ) ;
$rejectTypes = array ( 'html' , 'enum' , 'text' ) ;
for ( $i = 0 ; isset ( $_POST [ 'group_' . $i ] ) && $i < 2 ; $i ++ )
{
//echo "\n***group-$i Size:".sizeof($_POST['group_' . $i])."\n";
foreach ( $_POST [ 'group_' . $i ] as $field )
{
$fieldname = strtoupper ( $field ) ;
//originalListViewDefs are all lower case
$lowerFieldName = strtolower ( $field ) ;
if (isset ( $this->originalListViewDefs [ $lowerFieldName ] ))
{
$fields [ $fieldname ] = $this->originalListViewDefs [ $lowerFieldName ] ;
} else
{
//check if we have the case wrong for custom fields
if (! isset ( $this->module->field_defs [ $fieldname ] ))
{
foreach ( $this->module->field_defs as $key => $value )
{
if (strtoupper ( $key ) == $fieldname)
{
$fields [ $fieldname ] = array ( 'width' => 10 , 'label' => $this->module->field_defs [ $key ] [ 'vname' ] ) ;
break ;
}
}
} else
{
$fields [ $fieldname ] = array ( 'width' => 10 , 'label' => $this->module->field_defs [ $fieldname ] [ 'vname' ] ) ;
}
// sorting fields of certain types will cause a database engine problems
// we only check this for custom fields, as we assume that OOB fields have been independently confirmed as ok
if (isset ( $this->module->field_defs [ strtolower ( $fieldname ) ] ) && (in_array ( $this->module->field_defs [ strtolower ( $fieldname ) ] [ 'type' ], $rejectTypes ) || isset($this->module->field_defs [ strtolower ( $fieldname ) ]['custom_module'])))
{
$fields [ $fieldname ] [ 'sortable' ] = false ;
}
// Bug 23728 - Make adding a currency type field default to setting the 'currency_format' to true
if (isset ( $this->module->field_defs [ strtolower ( $fieldname ) ] [ 'type ' ] ) && $this->module->field_defs [ strtolower ( $fieldname ) ] [ 'type' ] == 'currency')
{
$fields [ $fieldname ] [ 'currency_format' ] = true;
}
}
if (isset ( $_REQUEST [ strtolower ( $fieldname ) . 'width' ] ))
{
$width = substr ( $_REQUEST [ strtolower ( $fieldname ) . 'width' ], 6, 3 ) ;
if (strpos ( $width, "%" ) != false)
{
$width = substr ( $width, 0, 2 ) ;
}
if ($width < 101 && $width > 0)
{
$fields [ $fieldname ] [ 'width' ] = $width ;
}
} else if (isset ( $this->listViewDefs [ $fieldname ] [ 'width' ] ))
{
$fields [ $fieldname ] [ 'width' ] = $this->listViewDefs [ $fieldname ] [ 'width' ] ;
}
//Get additional Data for relate fields
if (isset($this->module->field_defs [ strtolower ( $fieldname ) ] [ 'type' ]) && $this->module->field_defs [ strtolower ( $fieldname ) ] [ 'type' ] == 'relate') {
$fields [ $fieldname ] = $this->addRelateData($field, $fields [ $fieldname ]);
}
$fields [ $fieldname ] [ 'default' ] = ($i == 0) ;
}
}
// Add the reserved fields back in to the end of the default fields in the layout
// ASSUMPTION: reserved fields go back at the end
// First, load the reserved fields - we cannot assume that getDefaultFields has been called earlier when saving
$this->getDefaultFields();
foreach ( $this->reserved as $key => $def)
{
$fields[ $key ] = $def;
}
return $fields ;
}
function handleSave ()
{
$fields = $this->_loadLayoutFromRequest();
$this->_writeToFile($this->customFile,'ListView',$this->module_name,$fields,$this->_variables);
$GLOBALS [ "listViewDefs" ] [ $this->module_name ] = $fields ;
// now clear the cache so that the results are immediately visible
include_once ('include/TemplateHandler/TemplateHandler.php') ;
TemplateHandler::clearCache ( $this->module_name, "ListView.tpl" ) ; // not currently cached, but here for the future
}
}
?>

View File

@@ -0,0 +1,256 @@
<?php
if (! defined ( 'sugarEntry' ) || ! sugarEntry)
die ( 'Not A Valid Entry Point' ) ;
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
/*
* Modify an exising Subpanel
* Typically called from Studio for a deployed (OOB or installed custom module)
*
* Uses the listview editor to modify the subpanel
* Uses SubPanelDefinitions to load the definitions for the subpanel, and SubPanel to save them, using the unique SubPanel override mechanism
* There are two relevant modules for every subpanel - the module whose detailview this subpanel will appear in ($module_name), and the module that is the source of the data for the subpanel ($subPanelParentModule)
*/
require_once ('modules/ModuleBuilder/parsers/parser.modifylistview.php') ;
class ParserModifySubPanel extends ParserModifyListView
{
var $listViewDefs = false ;
var $defaults = array ( ) ;
var $additional = array ( ) ;
var $available = array ( ) ;
var $columns = array ( 'LBL_DEFAULT' => 'getDefaultFields' , 'LBL_HIDDEN' => 'getAvailableFields' ) ;
function init ($module_name , $subPanelName)
{
$GLOBALS [ 'log' ]->debug ( "in ParserModifySubPanel: module_name={$module_name} child_module={$subPanelName}" ) ;
$this->moduleName = $module_name ;
$this->subPanelName = $subPanelName ;
global $beanList, $beanFiles ;
// Sometimes we receive a module name which is not in the correct CamelCase, so shift to lower case for all beanList lookups
$beanListLower = array_change_key_case ( $beanList ) ;
// Retrieve the definitions for all the available subpanels for this module
$class = $beanListLower [ strtolower ( $this->moduleName ) ] ;
require_once ($beanFiles [ $class ]) ;
$module = new $class ( ) ;
require_once ('include/SubPanel/SubPanelDefinitions.php') ;
$spd = new SubPanelDefinitions ( $module ) ;
// Get the lists of fields already in the subpanel and those that can be added in
// Get the fields lists from an aSubPanel object describing this subpanel from the SubPanelDefinitions object
$this->originalListViewDefs = array ( ) ;
if (array_key_exists ( strtolower ( $this->subPanelName ), $spd->layout_defs [ 'subpanel_setup' ] ))
{
$originalPanel = $spd->load_subpanel ( $this->subPanelName, true ) ;
$this->originalListViewDefs = $originalPanel->get_list_fields () ;
$this->panel = $spd->load_subpanel ( $subPanelName, false ) ;
$this->listViewDefs = $this->panel->get_list_fields () ;
// Retrieve a copy of the bean for the parent module of this subpanel - so we can find additional fields for the layout
$subPanelParentModuleName = $this->panel->get_module_name () ;
$this->subPanelParentModule = null ;
if (! empty ( $subPanelParentModuleName ) && isset($beanListLower[strtolower($subPanelParentModuleName)]))
{
$class = $beanListLower[strtolower($subPanelParentModuleName)];
if (isset($beanFiles [ $class ]))
{
require_once ($beanFiles [ $class ]) ;
$this->subPanelParentModule = new $class ( ) ;
}
}
}
$this->language_module = $this->panel->template_instance->module_dir ;
}
/**
* Return a list of the fields that will be displayed in the subpanel
*/
function getDefaultFields ()
{
$this->defaults = array ( ) ;
foreach ( $this->listViewDefs as $key => $def )
{
if (! empty ( $def [ 'usage' ] ) && strcmp ( $def [ 'usage' ], 'query_only' ) == 0)
continue ;
if (! empty ( $def [ 'vname' ] ))
$def [ 'label' ] = $def [ 'vname' ] ;
$this->defaults [ $key ] = $def ;
}
return $this->defaults ;
}
/**
* Return a list of fields that are not currently included in the subpanel but that are available for use
*/
function getAvailableFields ()
{
$this->availableFields = array ( ) ;
if ($this->subPanelParentModule != null)
{
$lowerFieldList = array_change_key_case ( $this->listViewDefs ) ;
foreach ( $this->originalListViewDefs as $key => $def )
{
$key = strtolower ( $key ) ;
if (! isset ( $lowerFieldList [ $key ] ))
{
$this->availableFields [ $key ] = $def ;
}
}
$GLOBALS [ 'log' ]->debug ( 'parser.modifylistview.php->getAvailableFields(): field_defs=' . print_r ( $this->availableFields, true ) ) ;
foreach ( $this->subPanelParentModule->field_defs as $key => $fieldDefinition )
{
$fieldName = strtolower ( $key ) ;
if (! isset ( $lowerFieldList [ $fieldName ] )) // bug 16728 - check this first, so that other conditions (e.g., studio == visible) can't override and add duplicate entries
{
if ((empty ( $fieldDefinition [ 'source' ] ) || $fieldDefinition [ 'source' ] == 'db' || $fieldDefinition [ 'source' ] == 'custom_fields') && $fieldDefinition [ 'type' ] != 'id' && strcmp ( $fieldName, 'deleted' ) != 0 || (isset ( $def [ 'name' ] ) && strpos ( $def [ 'name' ], "_name" ) != false) || ! empty ( $def [ 'custom_type' ] ) && (empty ( $fieldDefinition [ 'dbType' ] ) || $fieldDefinition [ 'dbType' ] != 'id') && (empty ( $fieldDefinition [ 'dbtype' ] ) || $fieldDefinition [ 'dbtype' ] != 'id') || (! empty ( $fieldDefinition [ 'studio' ] ) && $fieldDefinition [ 'studio' ] == 'visible'))
{
$label = (isset ( $fieldDefinition [ 'vname' ] )) ? $fieldDefinition [ 'vname' ] : (isset ( $fieldDefinition [ 'label' ] ) ? $fieldDefinition [ 'label' ] : $fieldDefinition [ 'name' ]) ;
$this->availableFields [ $fieldName ] = array ( 'width' => '10' , 'label' => $label ) ;
}
}
}
}
return $this->availableFields ;
}
function getField ($fieldName)
{
foreach ( $this->listViewDefs as $key => $def )
{
$key = strtolower ( $key ) ;
if ($key == $fieldName)
{
return $def ;
}
}
foreach ( $this->originalListViewDefs as $key => $def )
{
$key = strtolower ( $key ) ;
if ($key == $fieldName)
{
return $def ;
}
}
foreach ( $this->panel->template_instance->field_defs as $key => $def )
{
$key = strtolower ( $key ) ;
if ($key == $fieldName)
{
return $def ;
}
}
return array ( ) ;
}
/*
* Save the modified definitions for a subpanel
* Obtains the field definitions from a _REQUEST array, and merges them with the other fields from the original definitions
* Uses the subpanel override mechanism from SubPanel to save them
*/
function handleSave ()
{
$GLOBALS [ 'log' ]->debug ( "in ParserModifySubPanel->handleSave()" ) ;
require_once ('include/SubPanel/SubPanel.php') ;
$subpanel = new SubPanel ( $this->moduleName, 'fab4', $this->subPanelName, $this->panel ) ;
$newFields = array ( ) ;
foreach ( $this->listViewDefs as $name => $field )
{
if (! isset ( $field [ 'usage' ] ) || $field [ 'usage' ] != 'query_only')
{
$existingFields [ $name ] = $field ;
} else
{
$newFields [ $name ] = $field ;
}
}
// Loop through all of the fields defined in the 'Default' group of the ListView data in $_REQUEST
// Replace the field specification in the originalListViewDef with this new updated specification
foreach ( $_REQUEST [ 'group_0' ] as $field )
{
if (! empty ( $this->originalListViewDefs [ $field ] ))
{
$newFields [ $field ] = $this->originalListViewDefs [ $field ] ;
} else
{
$vname = '' ;
if (isset ( $this->panel->template_instance->field_defs [ $field ] ))
{
$vname = $this->panel->template_instance->field_defs [ $field ] [ 'vname' ] ;
}
if (($this->subPanelParentModule != null) && (isset ( $this->subPanelParentModule->field_name_map [ $field ] ) && ($this->subPanelParentModule->field_name_map [ $field ] [ 'type' ] == 'bool' || (isset ( $this->subPanelParentModule->field_name_map [ $field ] [ 'custom_type' ] ) && $this->subPanelParentModule->field_name_map [ $field ] [ 'custom_type' ] == 'bool'))))
{
$newFields [ $field ] = array ( 'name' => $field , 'vname' => $vname , 'widget_type' => 'checkbox' ) ;
} else
{
$newFields [ $field ] = array ( 'name' => $field , 'vname' => $vname ) ;
}
}
// Now set the field width if specified in the $_REQUEST data
if (isset ( $_REQUEST [ strtolower ( $field ) . 'width' ] ))
{
$width = substr ( $_REQUEST [ strtolower ( $field ) . 'width' ], 6, 3 ) ;
if (strpos ( $width, "%" ) != false)
{
$width = substr ( $width, 0, 2 ) ;
}
if ($width < 101 && $width > 0)
{
$newFields [ $field ] [ 'width' ] = $width ;
}
} else if (isset ( $this->listViewDefs [ $field ] [ 'width' ] ))
{
$newFields [ $field ] [ 'width' ] = $this->listViewDefs [ $field ] [ 'width' ] ;
}
}
$subpanel->saveSubPanelDefOverride ( $this->panel, 'list_fields', $newFields ) ;
}
}
?>

View File

@@ -0,0 +1,658 @@
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
/*
* A mechanism to dynamically define new Relationships between modules
* This differs from the classes in modules/Relationships and data/Link in that they contain the implementation for pre-defined Relationships
* Those classes use the metadata in the dictionary and layout definitions to implement the relationships; this class allows you to manage and manipulate that metadata
*/
class AbstractRelationship
{
protected $definition ; // enough information to rebuild this relationship
/*
* These are the elements that fully define any Relationship
* Any subclass of AbstractRelationship uses an array with a subset of the following keys as metadata to describe the Relationship it will implement
* The base set of keys are those used in the Relationships table
* Defined as Public as MBRelationship uses these to read the _POST data
*/
public static $definitionKeys = array (
// atttributes of this relationship - here in the definition so they are preserved across saves and loads
'for_activities',
'is_custom',
'from_studio',
'readonly' , // a readonly relationship cannot be Built by subclasses of AbstractRelationships
'deleted' , // a deleted relationship will not be built, and if it had been built previously the built relationship will be removed
'relationship_only' , // means that we won't build any UI components for this relationship - required while the Subpanel code is restricted to one subpanel only from any module, and probably useful afterwards also for developers to build relationships for new code - it's a feature!
// keys not found in Relationships table
'label' , // optional
'rhs_label', // optional
'lhs_label', // optional
'lhs_subpanel' , // subpanel FROM the lhs_module to display on the rhs_module detail view
'rhs_subpanel' , // subpanel FROM the rhs_module to display on the lhs_module detail view
// keys from Relationships table
'relationship_name' ,
'lhs_module' ,
'lhs_table' ,
'lhs_key' ,
'rhs_module' ,
'rhs_table' ,
'rhs_key' ,
'join_table' ,
'join_key_lhs' ,
'join_key_rhs' ,
'relationship_type' ,
'relationship_role_column' ,
'relationship_role_column_value' ,
'reverse' ) ;
/*
* Relationship_role_column and relationship_role_column_value:
* These two values define an additional condition on the relationship. If present, the value in relationship_role_column in the relationship table must equal relationship_role_column_value
* Any update to the relationship made using a link field tied to the relationship (as is proper) will automatically (in Link.php) add in the relationship_role_column_value
* The relationship table must of course contain a column with the name given in relationship_role_column
*
* relationship_role_column and relationship_role_column_value are here implemented in a slightly less optimized form than in the standard OOB application
* In the OOB application, multiple relationships can, and do, share the same relationship table. Therefore, each variant of the relationship does not require its own table
* Here for simplicity in implementation each relationship has its own unique table. Therefore, the relationship_role_column in these tables will only contain the value relationship_role_column_value
* In the OOB relationships, the relationship_role_column will contain any of the relationship_role_column_values from the relationships that share the table
* TODO: implement this optimization
*
*/
/*
* Constructor
* @param string $definition Definition array for this relationship. Parameters are given in self::keys
*/
function __construct ($definition)
{
// set any undefined attributes to the default value
foreach ( array ( 'readonly' , 'deleted' , 'relationship_only', 'for_activities', 'is_custom', 'from_studio' ) as $key )
if (! isset ( $definition [ $key ] ))
$definition [ $key ] = false ;
foreach ( self::$definitionKeys as $key )
{
$this->$key = isset ( $definition [ $key ] ) ? $definition [ $key ] : '' ;
}
$this->definition = $definition ;
}
/*
* Get the unique name of this relationship
* @return string The unique name (actually just that given to the constructor)
*/
public function getName ()
{
return isset ( $this->definition [ 'relationship_name' ] ) ? $this->definition [ 'relationship_name' ] : null ;
}
public function setName ($relationshipName)
{
$this->relationship_name = $this->definition [ 'relationship_name' ] = $relationshipName ;
}
/*
* Is this relationship readonly or not?
* @return boolean True if cannot be changed; false otherwise
*/
public function readonly ()
{
return $this->definition [ 'readonly' ] ;
}
public function setReadonly ()
{
$this->readonly = $this->definition [ 'readonly' ] = true ;
}
public function setFromStudio ()
{
$this->from_studio = $this->definition [ 'from_studio' ] = true ;
}
/*
* Has this relationship been deleted? A deleted relationship does not get built, and is no longer visible in the list of relationships
* @return boolean True if it has been deleted; false otherwise
*/
public function deleted ()
{
return $this->definition [ 'deleted' ] ;
}
public function delete ()
{
$this->deleted = $this->definition [ 'deleted' ] = true ;
}
public function getType ()
{
return $this->relationship_type ;
}
public function relationship_only ()
{
return $this->definition [ 'relationship_only' ] ;
}
public function setRelationship_only ()
{
$this->relationship_only = $this->definition [ 'relationship_only' ] = true ;
}
/*
* Get a complete description of this relationship, sufficient to pass back to a constructor to reestablish the relationship
* Each subclass must provide enough information in $this->definition for its constructor
* Used by UndeployedRelationships to save out a set of AbstractRelationship descriptions
* The format is the same as the schema for the Relationships table for convenience, and is defined in self::keys. That is,
* `relationship_name`, `lhs_module`, `lhs_table`, `lhs_key`, `rhs_module`, `rhs_table`,`rhs_key`, `join_table`, `join_key_lhs`, `join_key_rhs`, `relationship_type`, `relationship_role_column`, `relationship_role_column_value`, `reverse`,
* @return array Set of parameters to pass to an AbstractRelationship constructor - must contain at least ['relationship_type']='OneToOne' or 'OneToMany' or 'ManyToMany'
*/
function getDefinition ()
{
return $this->definition ;
}
/*
* BUILD methods called during the build
*/
/*
* Define the labels to be added to the module for the new relationships
* @return array An array of system value => display value
*/
function buildLabels ($update=false)
{
$labelDefinitions = array ( ) ;
if (!$this->relationship_only)
{
if(!$this->is_custom && $update && file_exists("modules/{$this->rhs_module}/metadata/subpaneldefs.php")){
include("modules/{$this->rhs_module}/metadata/subpaneldefs.php");
if(isset($layout_defs[$this->rhs_module]['subpanel_setup'][strtolower($this->lhs_module)]['title_key'])){
$rightSysLabel = $layout_defs[$this->rhs_module]['subpanel_setup'][strtolower($this->lhs_module)]['title_key'];
}
$layout_defs = array();
}
if(!$this->is_custom && $update && file_exists("modules/{$this->lhs_module}/metadata/subpaneldefs.php")){
include("modules/{$this->lhs_module}/metadata/subpaneldefs.php");
if(isset($layout_defs[$this->lhs_module]['subpanel_setup'][strtolower($this->rhs_module)]['title_key'])){
$leftSysLabel = $layout_defs[$this->lhs_module]['subpanel_setup'][strtolower($this->rhs_module)]['title_key'];
}
$layout_defs = array();
}
$labelDefinitions [] = array (
'module' => $this->rhs_module ,
'system_label' => isset($rightSysLabel)?$rightSysLabel : 'LBL_' . strtoupper ( $this->relationship_name . '_FROM_' . $this->getLeftModuleSystemLabel() ) . '_TITLE' ,
'display_label' => ($update && !empty($_REQUEST [ 'lhs_label' ] ))?$_REQUEST [ 'lhs_label' ] :(empty($this->lhs_label) ? translate ( $this->lhs_module ) : $this->lhs_label),
) ;
$labelDefinitions [] = array (
'module' => $this->lhs_module ,
'system_label' => isset($leftSysLabel)?$leftSysLabel :'LBL_' . strtoupper ( $this->relationship_name . '_FROM_' . $this->getRightModuleSystemLabel() ) . '_TITLE' ,
'display_label' => ($update && !empty($_REQUEST [ 'rhs_label' ] ))?$_REQUEST [ 'rhs_label' ] :(empty($this->rhs_label) ? translate ( $this->rhs_module ) : $this->rhs_label),
) ;
}
return $labelDefinitions ;
}
function getLeftModuleSystemLabel()
{
if($this->lhs_module == $this->rhs_module){
return $this->lhs_module.'_L';
}
return $this->lhs_module;
}
function getRightModuleSystemLabel()
{
if($this->lhs_module == $this->rhs_module){
return $this->rhs_module.'_R';
}
return $this->rhs_module;
}
/*
* GET methods called by the BUILD methods of the subclasses to construct the relationship metadata
*/
/*
* Build a description of a Subpanel that can be turned into an actual Subpanel by saveSubpanelDefinition in the implementation
* Note that we assume that the subpanel name we are given is valid - that is, a subpanel definition by that name exists, and that a module won't have attempt to define multiple subpanels with the same name
* Among the elements we construct is get_subpanel_data which is used as follows in SugarBean:
* $related_field_name = $this_subpanel->get_data_source_name();
* $parentbean->load_relationship($related_field_name);
* ...where $related_field_name must be the name of a link field that references the Relationship used to obtain the subpanel data
* @param string $sourceModule Name of the source module for this field
* @param string $relationshipName Name of the relationship
* @param string $subpanelName Name of the subpanel provided by the sourceModule
* @param string $titleKeyName Name of the subpanel title , if none, we will use the module name as the subpanel title.
*/
protected function getSubpanelDefinition ($relationshipName , $sourceModule , $subpanelName, $titleKeyName = '', $source = "")
{
if (empty($source))
$source = $relationshipName;
$subpanelDefinition = array ( ) ;
$subpanelDefinition [ 'order' ] = 100 ;
$subpanelDefinition [ 'module' ] = $sourceModule ;
$subpanelDefinition [ 'subpanel_name' ] = $subpanelName ;
// following two lines are required for the subpanel pagination code in ListView.php->processUnionBeans() to correctly determine the relevant field for sorting
$subpanelDefinition [ 'sort_order' ] = 'asc' ;
$subpanelDefinition [ 'sort_by' ] = 'id' ;
if(!empty($titleKeyName)){
$subpanelDefinition [ 'title_key' ] = 'LBL_' . strtoupper ( $relationshipName . '_FROM_' . $titleKeyName ) . '_TITLE' ;
}else{
$subpanelDefinition [ 'title_key' ] = 'LBL_' . strtoupper ( $relationshipName . '_FROM_' . $sourceModule ) . '_TITLE' ;
}
$subpanelDefinition [ 'get_subpanel_data' ] = $source ;
$subpanelDefinition [ 'top_buttons' ] = array(
array('widget_class' => "SubPanelTopButtonQuickCreate"),
array('widget_class' => 'SubPanelTopSelectButton', 'mode'=>'MultiSelect')
);
return array ( $subpanelDefinition );
}
/*
* Construct a first link id field for the relationship for use in Views
* It is used during the save from an edit view in SugarBean->save_relationship_changes(): for each relate field, $this->$linkfieldname->add( $this->$def['id_name'] )
* @param string $sourceModule Name of the source module for this field
* @param string $relationshipName Name of the relationship
*/
protected function getLinkFieldDefinition ($sourceModule , $relationshipName, $right_side = false, $vname = "")
{
$vardef = array ( ) ;
$vardef [ 'name' ] = $relationshipName ;
$vardef [ 'type' ] = 'link' ;
$vardef [ 'relationship' ] = $relationshipName ;
$vardef [ 'source' ] = 'non-db' ;
if ($right_side)
$vardef [ 'side' ] = 'right' ;
if (!empty($vname))
$vardef [ 'vname' ] = $vname;
return $vardef ;
}
/*
* Construct a second link id field for the relationship for use in Views
* It is used in two places:
* - the editview.tpl for Relate fields requires that a field with the same name as the relate field's id_name exists
* - it is loaded in SugarBean->fill_in_link_field while SugarBean processes the relate fields in fill_in_relationship_fields
* @param string $sourceModule Name of the source module for this field
* @param string $relationshipName Name of the relationship
*/
protected function getLink2FieldDefinition ($sourceModule , $relationshipName, $right_side = false, $vname = "")
{
$vardef = array ( ) ;
$vardef [ 'name' ] = $this->getIDName( $sourceModule ) ; // must match the id_name field value in the relate field definition
$vardef [ 'type' ] = 'link' ;
$vardef [ 'relationship' ] = $relationshipName ;
$vardef [ 'source' ] = 'non-db' ;
$vardef ['reportable'] = false;
if ($right_side)
$vardef [ 'side' ] = 'right' ;
else
$vardef [ 'side' ] = 'left' ;
if (!empty($vname))
$vardef [ 'vname' ] = $vname;
return $vardef ;
}
/*
* Construct a relate field for the vardefs
* The relate field is the element that is shown in the UI
* @param string $sourceModule Name of the source module for this field
* @param string $relationshipName Name of the relationship
* @param string $moduleType Optional - "Types" of the module - array of SugarObject types such as "file" or "basic"
*/
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 ;
}
/*
* Construct the contents of the Relationships MetaData entry in the dictionary for a generic relationship
* The entry we build will have three sections:
* 1. relationships: the relationship definition
* 2. table: name of the join table for this many-to-many relationship
* 3. fields: fields within the join table
* 4. indicies: indicies on the join table
* @param string $relationshipType Cardinality of the relationship, for example, MB_ONETOONE or MB_ONETOMANY or MB_MANYTOMANY
*/
function getRelationshipMetaData ($relationshipType)
{
$relationshipName = $this->definition [ 'relationship_name' ] ;
$lhs_module = $this->lhs_module ;
$rhs_module = $this->rhs_module ;
$lhs_table = $this->getTablename ( $lhs_module ) ;
$rhs_table = $this->getTablename ( $rhs_module ) ;
$properties = array ( ) ;
// first define section 1, the relationship element of the metadata entry
$rel_properties = array ( ) ;
$rel_properties [ 'lhs_module' ] = $lhs_module ;
$rel_properties [ 'lhs_table' ] = $lhs_table ;
$rel_properties [ 'lhs_key' ] = 'id' ;
$rel_properties [ 'rhs_module' ] = $rhs_module ;
$rel_properties [ 'rhs_table' ] = $rhs_table ;
$rel_properties [ 'rhs_key' ] = 'id' ;
// because the implementation of one-to-many relationships within SugarBean does not use a join table and so requires schema changes to add a foreign key for each new relationship,
// we currently implement all new relationships as many-to-many regardless of the real type and enforce cardinality through the relate fields and subpanels
$rel_properties [ 'relationship_type' ] = MB_MANYTOMANY ;
// but as we need to display the true cardinality in Studio and ModuleBuilder we also record the actual relationship type
// this property is only used by Studio/MB
$properties [ 'true_relationship_type' ] = $relationshipType ;
if ($this->from_studio)
$properties [ 'from_studio' ] = true;
$rel_properties [ 'join_table' ] = $this->getValidDBName ( $relationshipName."_c" ) ;
// a and b are in case the module relates to itself
$rel_properties [ 'join_key_lhs' ] = $this->getJoinKeyLHS() ;
$rel_properties [ 'join_key_rhs' ] = $this->getJoinKeyRHS() ;
// set the extended properties if they exist = for now, many-to-many definitions do not have to contain a role_column even if role_column_value is set; we'll just create a likely name if missing
if (isset ( $this->definition [ 'relationship_role_column_value' ] ))
{
if (! isset ( $this->definition [ 'relationship_role_column' ] ))
$this->definition [ 'relationship_role_column' ] = 'relationship_role_column' ;
$rel_properties [ 'relationship_role_column' ] = $this->definition [ 'relationship_role_column' ] ;
$rel_properties [ 'relationship_role_column_value' ] = $this->definition [ 'relationship_role_column_value' ] ;
}
$properties [ 'relationships' ] [ $relationshipName ] = $rel_properties ;
// construct section 2, the name of the join table
$properties [ 'table' ] = $rel_properties [ 'join_table' ] ;
// now construct section 3, the fields in the join table
$properties [ 'fields' ] [] = array ( 'name' => 'id' , 'type' => 'varchar' , 'len' => 36 ) ;
$properties [ 'fields' ] [] = array ( 'name' => 'date_modified' , 'type' => 'datetime' ) ;
$properties [ 'fields' ] [] = array ( 'name' => 'deleted' , 'type' => 'bool' , 'len' => '1' , 'default' => '0' , 'required' => true ) ;
$properties [ 'fields' ] [] = array ( 'name' => $rel_properties [ 'join_key_lhs' ] , 'type' => 'varchar' , 'len' => 36 ) ;
$properties [ 'fields' ] [] = array ( 'name' => $rel_properties [ 'join_key_rhs' ] , 'type' => 'varchar' , 'len' => 36 ) ;
if (strtolower ( $lhs_module ) == 'documents' || strtolower ( $rhs_module ) == 'documents' )
{
$properties [ 'fields' ] [] = array ( 'name' => 'document_revision_id' , 'type' => 'varchar' , 'len' => '36' ) ;
}
// if we have an extended relationship condition, then add in the corresponding relationship_role_column to the relationship (join) table
// for now this is restricted to extended relationships that can be specified by a varchar
if (isset ( $this->definition [ 'relationship_role_column_value' ] ))
{
$properties [ 'fields' ] [] = array ( 'name' => $this->definition [ 'relationship_role_column' ] , 'type' => 'varchar' ) ;
}
// finally, wrap up with section 4, the indices on the join table
$indexBase = $this->getValidDBName ( $relationshipName ) ;
$properties [ 'indices' ] [] = array ( 'name' => $indexBase . 'spk' , 'type' => 'primary' , 'fields' => array ( 'id' ) ) ;
switch ($relationshipType)
{
case MB_ONETOONE:
$alternateKeys = array () ;
$properties [ 'indices' ] [] = array ( 'name' => $indexBase . '_ida1' , 'type' => 'index' , 'fields' => array ( $rel_properties [ 'join_key_lhs' ] ) ) ;
$properties [ 'indices' ] [] = array ( 'name' => $indexBase . '_idb2' , 'type' => 'index' , 'fields' => array ( $rel_properties [ 'join_key_rhs' ] ) ) ;
break;
case MB_ONETOMANY :
$alternateKeys = array ( $rel_properties [ 'join_key_rhs' ] ) ;
$properties [ 'indices' ] [] = array ( 'name' => $indexBase . '_ida1' , 'type' => 'index' , 'fields' => array ( $rel_properties [ 'join_key_lhs' ] ) ) ;
break;
default:
$alternateKeys = array ( $rel_properties [ 'join_key_lhs' ] , $rel_properties [ 'join_key_rhs' ] ) ;
}
if (count($alternateKeys)>0)
$properties [ 'indices' ] [] = array ( 'name' => $indexBase . '_alt' , 'type' => 'alternate_key' , 'fields' => $alternateKeys ) ; // type must be set to alternate_key for Link.php to correctly update an existing record rather than inserting a copy - it uses the fields in this array as the keys to check if a duplicate record already exists
return $properties ;
}
/*
* UTILITY methods
*/
/*
* Method to build a name for a relationship between a module and an Activities submodule
* Used primarily in UndeployedRelationships to ensure that the subpanels we construct for Activities get their data from the correct relationships
* @param string $activitiesSubModuleName Name of the activities submodule, such as Tasks
*/
function getActivitiesSubModuleRelationshipName ( $activitiesSubModuleName )
{
return $this->lhs_module . "_" . strtolower ( $activitiesSubModuleName ) ;
}
/*
* Return a version of $proposed that can be used as a column name in any of our supported databases
* Practically this means no longer than 25 characters as the smallest identifier length for our supported DBs is 30 chars for Oracle plus we add on at least four characters in some places (for indicies for example)
* TODO: Ideally this should reside in DBHelper as it is such a common db function...
* @param string $name Proposed name for the column
* @param string $ensureUnique
* @return string Valid column name trimmed to right length and with invalid characters removed
*/
static function getValidDBName ($name, $ensureUnique = false)
{
require_once 'modules/ModuleBuilder/parsers/constants.php' ;
// first strip any invalid characters - all but alphanumerics and -
$name = preg_replace ( '/[^\w-]+/i', '', $name ) ;
$len = strlen ( $name ) ;
$result = $name;
if ($ensureUnique)
{
$md5str = md5($name);
$tail = substr ( $name, -11) ;
$temp = substr($md5str , strlen($md5str)-4 );
$result = substr ( $name, 0, 10) . $temp . $tail ;
}else if ($len > (MB_MAXDBIDENTIFIERLENGTH - 5))
{
$result = substr ( $name, 0, 11) . substr ( $name, 11 - MB_MAXDBIDENTIFIERLENGTH + 5);
}
return strtolower ( $result ) ;
}
/*
* Tidy up any provided relationship type - convert all the variants of a name to the canonical type - for example, One To Many = MB_ONETOMANY
* @param string $type Relationship type
* @return string Canonical type
*/
static function parseRelationshipType ($type)
{
$type = strtolower ( $type ) ;
$type = preg_replace ( '/[^\w]+/i', '', strtolower ( $type ) ) ;
$canonicalTypes = array ( ) ;
foreach ( array ( MB_ONETOONE , MB_ONETOMANY , MB_MANYTOMANY , MB_MANYTOONE) as $canonicalType )
{
if ($type == preg_replace ( '/[^\w]+/i', '', strtolower ( $canonicalType ) ))
return $canonicalType ;
}
// ok, we give up...
return MB_MANYTOMANY ;
}
function getJoinKeyLHS()
{
if (!isset($this->joinKeyLHS))
$this->joinKeyLHS = $this->getValidDBName ( $this->relationship_name . $this->lhs_module . "_ida" , true) ;
return $this->joinKeyLHS;
}
function getJoinKeyRHS()
{
if (!isset($this->joinKeyRHS))
$this->joinKeyRHS = $this->getValidDBName ( $this->relationship_name . $this->rhs_module . "_idb" , true) ;
return $this->joinKeyRHS;
}
/*
* Return the name of the ID field that will be used to link the subpanel, the link field and the relationship metadata
* @param string $sourceModule The name of the primary module in the relationship
* @return string Name of the id field
*/
function getIDName( $sourceModule )
{
return ($sourceModule == $this->lhs_module ) ? $this->getJoinKeyLHS() : $this->getJoinKeyRHS() ;
}
/*
* Return the name of a module's standard (non-cstm) table in the database
* @param string $moduleName Name of the module for which we are to find the table
* @return string Tablename
*/
protected function getTablename ($moduleName)
{
// Check the moduleName exists in the beanList before calling get_module_info - Activities is the main culprit here
if (isset ( $GLOBALS [ 'beanList' ] [ $moduleName ] ))
{
$module = get_module_info ( $moduleName ) ;
return $module->table_name ;
}
return strtolower ( $moduleName ) ;
}
public function getTitleKey($left=false){
if(!$this->is_custom && !$left && file_exists("modules/{$this->rhs_module}/metadata/subpaneldefs.php")){
include("modules/{$this->rhs_module}/metadata/subpaneldefs.php");
if(isset($layout_defs[$this->rhs_module]['subpanel_setup'][strtolower($this->lhs_module)]['title_key'])){
return $layout_defs[$this->rhs_module]['subpanel_setup'][strtolower($this->lhs_module)]['title_key'];
}
}else if(!$this->is_custom && file_exists("modules/{$this->lhs_module}/metadata/subpaneldefs.php")){
include("modules/{$this->lhs_module}/metadata/subpaneldefs.php");
if(isset($layout_defs[$this->lhs_module]['subpanel_setup'][strtolower($this->rhs_module)]['title_key'])){
return $layout_defs[$this->lhs_module]['subpanel_setup'][strtolower($this->rhs_module)]['title_key'];
}
}
if($left){
$titleKeyName = $this->getRightModuleSystemLabel();
$sourceModule = $this->rhs_module;
}else{
$titleKeyName = $this->getLeftModuleSystemLabel();
$sourceModule = $this->lhs_module;
}
if(!empty($titleKeyName)){
$title_key = 'LBL_' . strtoupper ( $this->relationship_name . '_FROM_' . $titleKeyName ) . '_TITLE' ;
}else{
$title_key = 'LBL_' . strtoupper ( $this->relationship_name . '_FROM_' . $sourceModule ) . '_TITLE' ;
}
return $title_key;
}
}

View File

@@ -0,0 +1,556 @@
<?php
if (! defined ( 'sugarEntry' ) || ! sugarEntry)
die ( 'Not A Valid Entry Point' ) ;
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
/*
* Abstract class for managing a set of Relationships
* The Relationships we're managing consist of metadata about relationships, rather than relationship implementations used by the application
* Relationships defined here are implemented by the build() method to become a relationship that the application can use
* Note that the modules/Relationships/Relationship.php contains some methods that look similar; remember though that the methods in that file are acting on implemented relationships, not the metadata that we deal with here
*/
class AbstractRelationships
{
static $methods = array ( 'Labels' => 'language' , 'RelationshipMetaData' => 'relationships' , 'SubpanelDefinitions' => 'layoutdefs' , 'Vardefs' => 'vardefs' , 'FieldsToLayouts' => 'layoutfields' ) ;
static $activities = array ( 'calls' => 'Calls' , 'meetings' => 'Meetings' , 'notes' => 'Notes' , 'tasks' => 'Tasks' , 'emails' => 'Emails' ) ;
protected $relationships = array ( ) ; // array containing all the AbstractRelationship objects that are in this set of relationships
protected $moduleName ;
/*
* Find all deployed modules that can participate in a relationship
* Return a list of modules with associated subpanels
* @param boolean $includeActivitiesSubmodules True if the list should include Calls, Meetings etc; false if they should be replaced by the parent, Activities
* @return array Array of [$module][$subpanel]
*/
static function findRelatableModules ($includeActivitiesSubmodules = true)
{
$relatableModules = array ( ) ;
// add in activities automatically if required
$relatableModules [ 'Activities' ] [ 'default' ] = translate( 'LBL_DEFAULT' ) ;
// find all deployed modules
require_once 'modules/ModuleBuilder/Module/StudioBrowser.php' ;
$browser = new StudioBrowser() ;
$browser->loadRelatableModules();
reset($browser->modules) ;
while ( list( $moduleName , $module ) = each($browser->modules) )
{
// do not include the submodules of Activities as already have the parent...
if (! $includeActivitiesSubmodules && in_array ( $module->module, self::$activities ))
continue ;
$relatableModules [ $module->module ] = $module->getProvidedSubpanels() ;
}
return $relatableModules ;
}
static function validSubpanel ($filename)
{
if (! file_exists ( $filename ))
return false ;
include $filename ;
return (isset ( $subpanel_layout ) && (isset ( $subpanel_layout [ 'top_buttons' ] ) && isset ( $subpanel_layout [ 'list_fields' ] ))) ;
}
/*
* Get a list of all relationships (which have not been deleted)
* @return array Array of relationship names, ready for use in get()
*/
function getRelationshipList ()
{
$list = array ( ) ;
foreach ( $this->relationships as $name => $relationship )
{
if (! $relationship->deleted ())
$list [ $name ] = $name ;
}
return $list ;
}
/*
* Get a relationship by name
* @param string $relationshipName The unique name for this relationship, as returned by $relationship->getName()
* @return AbstractRelationship or false if $relationshipName is not in this set of relationships
*/
function get ($relationshipName)
{
if (isset ( $this->relationships [ $relationshipName ] ))
{
return $this->relationships [ $relationshipName ] ;
}
return false ;
}
/*
* Construct a relationship from the information in the $_REQUEST array
* If a relationship_name is provided, and that relationship is not read only, then modify the existing relationship, overriding the definition with any AbstractRelationship::$definitionkeys entries set in the $_REQUEST
* Otherwise, create and add a new relationship with the information in the $_REQUEST
* @return AbstractRelationship
*/
function addFromPost ()
{
$definition = array ( ) ;
require_once 'modules/ModuleBuilder/parsers/relationships/AbstractRelationship.php' ;
foreach ( AbstractRelationship::$definitionKeys as $key )
{
if (! empty ( $_REQUEST [ $key ] ))
{
$definition [ $key ] = ($key == 'relationship_type') ? AbstractRelationship::parseRelationshipType ( $_REQUEST [ $key ] ) : $_REQUEST [ $key ] ;
}
}
// if this is a change to an existing relationship, and it is not readonly, then delete the old one
if (! empty ( $_REQUEST [ 'relationship_name' ] ))
{
if ($relationship = $this->get ( $_REQUEST [ 'relationship_name' ] ))
{
unset( $definition[ 'relationship_name' ] ) ; // in case the related modules have changed; this name is probably no longer appropriate
if (! $relationship->readonly ())
$this->delete ( $_REQUEST [ 'relationship_name' ] ) ;
}
}
$newRelationship = RelationshipFactory::newRelationship ( $definition ) ;
// TODO: error handling in case we get a badly formed definition and hence relationship
$this->add ( $newRelationship ) ;
return $newRelationship ;
}
/*
* Add a relationship to the set
* @param AbstractRelationship $relationship The relationship to add
*/
function add ($relationship)
{
$name = $this->getUniqueName ( $relationship ) ;
$relationship->setName ( $name ) ;
$this->relationships [ $name ] = $relationship ;
}
/*
* Load a set of relationships from a file
* The saved relationships are stored as AbstractRelationship objects, which isn't the same as the old MBRelationships definition
* @param string $basepath Base directory in which to store the relationships information
* @return Array of AbstractRelationship objects
*/
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 ;
}
/*
* Save the set of relationships to a file
* @param string $basepath Base directory in which to store the relationships information
*/
protected function _save ($relationships , $basepath)
{
$GLOBALS [ 'log' ]->info ( get_class ( $this ) . ": saving relationships to " . $basepath . '/relationships.php' ) ;
$header = file_get_contents ( 'modules/ModuleBuilder/MB/header.php' ) ;
$definitions = array ( ) ;
foreach ( $relationships as $relationship )
{
// if (! $relationship->readonly ())
$definitions [ $relationship->getName () ] = $relationship->getDefinition () ;
}
mkdir_recursive ( $basepath ) ;
// replace any existing relationships.php
write_array_to_file ( 'relationships', $definitions, $basepath . '/relationships.php', 'w', $header ) ;
}
/*
* Return all known deployed relationships
* All are set to read-only - the assumption for now is that we can't directly modify a deployed relationship
* However, if it was created through this AbstractRelationships class a modifiable version will be held in the relationships working file,
* and that one will override the readonly version in load()
*
* TODO: currently we ignore the value of the 'reverse' field in the relationships definition. This is safe to do as only one
* relationship (products-products) uses it (and there it makes no difference from our POV) and we don't use it when creating new ones
* @return array Array of $relationshipName => $relationshipDefinition as an array
*/
protected function getDeployedRelationships ()
{
$db = DBManagerFactory::getInstance () ;
$query = "SELECT * FROM relationships WHERE deleted = 0" ;
$result = $db->query ( $query ) ;
while ( $row = $db->fetchByAssoc ( $result ) )
{
// set this relationship to readonly
$row [ 'readonly' ] = true ;
$relationships [ $row [ 'relationship_name' ] ] = $row ;
}
return $relationships ;
}
/*
* Get a name for this relationship that is unique across all of the relationships we are aware of
* We make the name unique by simply adding on a suffix until we achieve uniqueness
* @param AbstractRelationship The relationship object
* @return string A globally unique relationship name
*/
protected function getUniqueName ($relationship)
{
$allRelationships = $this->getRelationshipList () ;
$basename = $relationship->getName () ;
if (empty ( $basename ))
{
// start off with the proposed name being simply lhs_module_rhs_module
$definition = $relationship->getDefinition () ;
$basename = strtolower ( $definition [ 'lhs_module' ] . '_' . $definition [ 'rhs_module' ] ) ;
}
$name = $basename ;
$suffix = 1 ;
while ( isset ( $allRelationships [ $name ] ) )
{
$name = $basename . "_" . ( string ) ($suffix ++) ;
}
return $name ;
}
/*
* Translate the set of relationship objects into files that the Module Loader can work with
* @param string $basepath Pathname of the directory to contain the build
* @param string $installDefPrefix Pathname prefix for the installdefs, for example for ModuleBuilder use "<basepath>/SugarModules"
* @param array $relationships Relationships to implement
*/
protected function build ($basepath , $installDefPrefix , $relationships )
{
global $sugar_config;
// keep the relationships data separate from any other build data by ading /relationships to the basepath
$basepath .= '/relationships' ;
$installDefs = array ( ) ;
$compositeAdded = false ;
foreach ( self::$methods as $method => $key )
{
$buildMethod = 'build' . $method ;
$saveMethod = 'save' . $method ;
foreach ( $relationships as $name => $relationship )
{
if (! ($relationship->readonly () || $relationship->deleted ()))
{
if (method_exists ( $relationship, $buildMethod ) && method_exists ( $this, $saveMethod ))
{
$metadata = $relationship->$buildMethod () ;
if (count ( $metadata ) > 0) // don't clutter up the filesystem with empty files...
{
$GLOBALS [ 'log' ]->debug ( get_class ( $this ) . ": BUILD is running METHOD $saveMethod" ) ;
$installDef = $this->$saveMethod ( $basepath, $installDefPrefix, $name, $metadata ) ;
// some save methods (e.g., saveRelateFieldDefinition) handle the installDefs internally and so return null
if (! is_null ( $installDef ))
{
foreach ( $installDef as $moduleName => $def )
{
$installDefs [ $key ] [ ] = $def ;
}
}
}
}
}
}
}
return $installDefs ;
}
/*
* SAVE methods called during the build to translate the metadata provided by each relationship into files for the module installer
* Note that the installer expects only one file for each module in each section of the manifest - multiple files result in only the last one being implemented!
*/
/*
* Add a set of labels to the module
* @param string $basepath Basepath location for this module
* @param $installDefPrefix Pathname prefix for the installdefs, for example for ModuleBuilder use "<basepath>/SugarModules"
* @param string $relationshipName Name of this relationship (for uniqueness)
* @param array $labelDefinitions Array of System label => Display label pairs
* @return null Nothing to be added to the installdefs for an undeployed module
*/
protected function saveLabels ($basepath , $installDefPrefix , $relationshipName , $labelDefinitions)
{
global $sugar_config;
mkdir_recursive ( "$basepath/language" ) ;
$headerString = "<?php\n//THIS FILE IS AUTO GENERATED, DO NOT MODIFY\n" ;
$installDefs = array ( ) ;
foreach ( $labelDefinitions as $definition )
{
$mod_strings = array();
$app_list_strings = array();
$out = $headerString;
$filename = "{$basepath}/language/{$definition['module']}.php" ;
if (file_exists ( $filename ))
include ($filename);
//Check for app strings
$GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->saveLabels(): saving the following to {$filename}"
. print_r ( $definition, true ) ) ;
if ($definition['module'] == 'application') {
$app_list_strings[$definition [ 'system_label' ]] = $definition [ 'display_label' ];
foreach ($app_list_strings as $key => $val)
$out .= override_value_to_string_recursive2('app_list_strings', $key, $val);
} else {
$mod_strings[ $definition [ 'system_label' ]] = $definition [ 'display_label' ];
foreach ($mod_strings as $key => $val)
$out .= override_value_to_string_recursive2('mod_strings', $key, $val);
}
$fh = fopen ( $filename, 'w' ) ;
fputs ( $fh, $out, strlen ( $out ) ) ;
fclose ( $fh ) ;
foreach($sugar_config['languages'] as $lk => $lv)
{
$installDefs [ $definition [ 'module' ] . "_$lk" ] = array (
'from' => "{$installDefPrefix}/relationships/language/{$definition [ 'module' ]}.php" ,
'to_module' => $definition [ 'module' ] ,
'language' => $lk
) ;
}
/* do not use the following write_array_to_file method to write the label file -
* module installer appends each of the label files together (as it does for all files)
* into a combined label file and so the last $mod_strings is the only one received by the application */
// write_array_to_file ( 'mod_strings', array ( $definition [ 'system_label' ] => $definition [ 'display_label' ] ), $filename, "a" ) ;
}
return $installDefs ;
}
/*
* Translate a set of relationship metadata definitions into files for the Module Loader
* @param string $basepath Basepath location for this module
* @param $installDefPrefix Pathname prefix for the installdefs, for example for ModuleBuilder use "<basepath>/SugarModules"
* @param string $relationshipName Name of this relationship (for uniqueness)
* @param array $relationshipMetaData Set of metadata definitions in the form $relationshipMetaData[$relationshipName]
* @return array $installDefs Set of new installDefs
*/
protected function saveRelationshipMetaData ($basepath , $installDefPrefix , $relationshipName , $relationshipMetaData)
{
mkdir_recursive ( "$basepath/relationships" ) ;
$installDefs = array ( ) ;
list ( $rhs_module, $properties ) = each ( $relationshipMetaData ) ;
$filename = "$basepath/relationships/{$relationshipName}MetaData.php" ;
$GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->saveRelationshipMetaData(): saving the following to {$filename}" . print_r ( $properties, true ) ) ;
write_array_to_file ( 'dictionary["' . $relationshipName . '"]', $properties, "{$filename}", 'w' ) ;
$installDefs [ $relationshipName ] = array ( /*'module' => $rhs_module , 'module_vardefs' => "<basepath>/Vardefs/{$relationshipName}.php" ,*/ 'meta_data' => "{$installDefPrefix}/relationships/relationships/{$relationshipName}MetaData.php" ) ;
return $installDefs ;
}
/*
* Translate a set of subpanelDefinitions into files for the Module Loader
* @param string $basepath Basepath location for this module
* @param $installDefPrefix Pathname prefix for the installdefs, for example for ModuleBuilder use "<basepath>/SugarModules"
* @param array $subpanelDefinitions Set of subpanel definitions in the form $subpanelDefinitions[$for_module][]
* @return array $installDefs Set of new installDefs
*/
protected function saveSubpanelDefinitions ($basepath , $installDefPrefix , $relationshipName , $subpanelDefinitions)
{
mkdir_recursive ( "$basepath/layoutdefs/" ) ;
foreach ( $subpanelDefinitions as $moduleName => $definitions )
{
$filename = "$basepath/layoutdefs/{$relationshipName}_{$moduleName}.php" ;
$out = "<?php\n// created: " . date('Y-m-d H:i:s') . "\n";
foreach ( $definitions as $definition )
{
$GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->saveSubpanelDefinitions(): saving the following to {$filename}" . print_r ( $definition, true ) ) ;
if (empty($definition ['get_subpanel_data']) || $definition ['subpanel_name'] == 'history' || $definition ['subpanel_name'] == 'activities') {
$definition ['get_subpanel_data'] = $definition ['subpanel_name'];
}
$out .= '$layout_defs["' . $moduleName . '"]["subpanel_setup"]["' . $definition ['get_subpanel_data'] . '"] = '
. var_export_helper($definition) . ";\n";
}
file_put_contents($filename, $out);
$installDefs [ $moduleName ] = array ( 'from' => "{$installDefPrefix}/relationships/layoutdefs/{$relationshipName}_{$moduleName}.php" , 'to_module' => $moduleName ) ;
}
return $installDefs ;
}
/*
* Translate a set of linkFieldDefinitions into files for the Module Loader
* Note that the Module Loader will only accept one entry in the vardef section of the Manifest for each module
* This means that we cannot simply build a file for each relationship as relationships that involve the same module will end up overwriting each other when installed
* So we have to append the vardefs for each relationship to a single file for each module
* @param string $basepath Basepath location for this module
* @param $installDefPrefix Pathname prefix for the installdefs, for example for ModuleBuilder use "<basepath>/SugarModules"
* @param string $relationshipName Name of this relationship (for uniqueness)
* @param array $linkFieldDefinitions Set of link field definitions in the form $linkFieldDefinitions[$for_module]
* @return array $installDefs Set of new installDefs
*/
protected function saveVardefs ($basepath , $installDefPrefix , $relationshipName , $vardefs)
{
mkdir_recursive ( "$basepath/vardefs/" ) ;
$GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->saveVardefs(): vardefs =" . print_r ( $vardefs, true ) ) ;
foreach ( $vardefs as $moduleName => $definitions )
{
// find this module's Object name - the object name, not the module name, is used as the key in the vardefs...
if (isset ( $GLOBALS [ 'beanList' ] [ $moduleName ] ))
{
$module = get_module_info ( $moduleName ) ;
$object = $module->object_name ;
} else
{
$object = $moduleName ;
}
$relName = $moduleName;
foreach ( $definitions as $definition )
{
if (!empty($definition['relationship']))
{
$relName = $definition['relationship'];
break;
}
}
$filename = "$basepath/vardefs/{$relName}_{$moduleName}.php" ;
$out = "<?php\n// created: " . date('Y-m-d H:i:s') . "\n";
foreach ( $definitions as $definition )
{
$GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->saveVardefs(): saving the following to {$filename}" . print_r ( $definition, true ) ) ;
$out .= '$dictionary["' . $object . '"]["fields"]["' . $definition [ 'name' ] . '"] = '
. var_export_helper($definition) . ";\n";
}
file_put_contents($filename, $out);
$installDefs [ $moduleName ] = array (
'from' => "{$installDefPrefix}/relationships/vardefs/{$relName}_{$moduleName}.php" ,
'to_module' => $moduleName
) ;
}
$GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->saveVardefs(): installDefs =" . print_r ( $installDefs, true ) ) ;
return $installDefs ;
}
/*
* Determine if we're dealing with a deployed or undeployed module based on the name
* Undeployed modules are those known to ModuleBuilder; the twist is that the deployed names of modulebuilder modules are keyname_modulename not packagename_modulename
* and ModuleBuilder doesn't have any accessor methods based around keys, so we must convert keynames to packagenames
* @param $deployedName Name of the module in the deployed form - that is, keyname_modulename or modulename
* @return array ('moduleName'=>name, 'packageName'=>package) if undeployed, ('moduleName'=>name) if deployed
*/
static function parseDeployedModuleName ($deployedName)
{
require_once 'modules/ModuleBuilder/MB/ModuleBuilder.php' ;
$mb = new ModuleBuilder ( ) ;
$packageName = '' ;
$moduleName = $deployedName ;
foreach ( $mb->getPackageList () as $name )
{
// convert the keyName into a packageName, needed for checking to see if this is really an undeployed module, or just a module with a _ in the name...
$package = $mb->getPackage ( $name ) ; // seem to need to call getPackage twice to get the key correctly... TODO: figure out why...
$key = $mb->getPackage ( $name )->key ;
if (strlen ( $key ) < strlen ( $deployedName ))
{
$position = stripos ( $deployedName, $key ) ;
$moduleName = trim( substr( $deployedName , strlen($key) ) , '_' ); //use trim rather than just assuming that _ is between packageName and moduleName in the deployedName
if ( $position !== false && $position == 0 && (isset ( $mb->packages [ $name ]->modules [ $moduleName ] )))
{
$packageName = $name ;
break ;
}
}
}
if (! empty ( $packageName ))
{
return array ( 'moduleName' => $moduleName , 'packageName' => $packageName ) ;
} else
{
return array ( 'moduleName' => $deployedName ) ;
}
}
}

View File

@@ -0,0 +1,275 @@
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
require_once 'modules/ModuleBuilder/parsers/relationships/OneToManyRelationship.php' ;
/*
* Class to manage the metadata for a One-To-Many Relationship
* The One-To-Many relationships created by this class are a combination of a subpanel and a custom relate field
* The LHS (One) module will receive a new subpanel for the RHS module. The subpanel gets its data ('get_subpanel_data') from a link field that references a new Relationship
* The RHS (Many) module will receive a new relate field to point back to the LHS
*
* OOB modules implement One-To-Many relationships as:
*
* On the LHS (One) side:
* A Relationship of type one-to-many in the rhs modules vardefs.php
* A link field in the same vardefs.php with 'relationship'= the relationship name and 'source'='non-db'
* A subpanel which gets its data (get_subpanel_data) from the link field
*
* On the RHS (Many) side:
* A Relate field in the vardefs, formatted as in this example, which references a link field:
* 'name' => 'account_name',
* 'rname' => 'name',
* 'id_name' => 'account_id',
* 'vname' => 'LBL_ACCOUNT_NAME',
* 'join_name'=>'accounts',
* 'type' => 'relate',
* 'link' => 'accounts',
* 'table' => 'accounts',
* 'module' => 'Accounts',
* 'source' => 'non-db'
* A link field which references the shared Relationship
*/
class ActivitiesRelationship extends OneToManyRelationship
{
protected static $subpanelsAdded = array();
protected static $labelsAdded = array();
/*
* Constructor
* @param array $definition Parameters passed in as array defined in parent::$definitionKeys
* The lhs_module value is for the One side; the rhs_module value is for the Many
*/
function __construct ($definition)
{
parent::__construct ( $definition ) ;
}
/*
* BUILD methods called during the build
*/
/*
* Define the labels to be added to the module for the new relationships
* @return array An array of system value => display value
*/
function buildLabels ()
{
$labelDefinitions = array ( ) ;
if (!$this->relationship_only )
{
if (!isset(ActivitiesRelationship::$labelsAdded[$this->lhs_module])) {
$labelDefinitions [] = array (
'module' => 'application' ,
'system_label' => 'parent_type_display',
'display_label' => array ( $this->lhs_module => ucfirst ( $this->lhs_module ))
) ;
$labelDefinitions [] = array (
'module' => 'application' ,
'system_label' => 'record_type_display',
'display_label' => array ( $this->lhs_module => ucfirst ( $this->lhs_module ))
) ;
$labelDefinitions [] = array (
'module' => 'application' ,
'system_label' => 'record_type_display_notes',
'display_label' => array ( $this->lhs_module => ucfirst ( $this->lhs_module ))
) ;
}
$labelDefinitions [] = array (
'module' => $this->lhs_module ,
'system_label' => 'LBL_' . strtoupper ( $this->relationship_name . '_FROM_' . $this->getRightModuleSystemLabel() ) . '_TITLE' ,
'display_label' => /*'*' .*/ ucfirst ( $this->rhs_module )
) ;
ActivitiesRelationship::$labelsAdded[$this->lhs_module] = true;
}
return $labelDefinitions ;
}
/*
* @return array An array of field definitions, ready for the vardefs, keyed by module
*/
function buildVardefs ( )
{
$vardefs = array ( ) ;
$vardefs [ $this->rhs_module ] [] = $this->getLinkFieldDefinition ( $this->lhs_module, $this->relationship_name ) ;
$vardefs [ $this->lhs_module ] [] = $this->getLinkFieldDefinition ( $this->rhs_module, $this->relationship_name ) ;
return $vardefs ;
}
protected function getLinkFieldDefinition ($sourceModule , $relationshipName)
{
$vardef = array ( ) ;
$vardef [ 'name' ] = $relationshipName;
$vardef [ 'type' ] = 'link' ;
$vardef [ 'relationship' ] = $relationshipName ;
$vardef [ 'source' ] = 'non-db' ;
return $vardef ;
}
/*
* Define what fields to add to which modules layouts
* @return array An array of module => fieldname
*/
function buildFieldsToLayouts ()
{
if ($this->relationship_only)
return array () ;
return array( $this->rhs_module => $this->relationship_name . "_name" ) ; // this must match the name of the relate field from buildVardefs
}
function buildSubpanelDefinitions ()
{
if ($this->relationship_only || isset(ActivitiesRelationship::$subpanelsAdded[$this->lhs_module]))
return array () ;
ActivitiesRelationship::$subpanelsAdded[$this->lhs_module] = true;
$relationshipName = substr($this->relationship_name, 0, strrpos($this->relationship_name, '_'));
return array( $this->lhs_module => array (
'activities' => $this->buildActivitiesSubpanelDefinition ( $relationshipName ),
'history' => $this->buildHistorySubpanelDefinition ( $relationshipName ) ,
));
}
/*
* @return array An array of relationship metadata definitions
*/
function buildRelationshipMetaData ()
{
$relationshipName = $this->definition [ 'relationship_name' ];
$relMetadata = array ( ) ;
$relMetadata [ 'lhs_module' ] = $this->definition [ 'lhs_module' ] ;
$relMetadata [ 'lhs_table' ] = $this->getTablename($this->definition [ 'lhs_module' ]) ;
$relMetadata [ 'lhs_key' ] = 'id' ;
$relMetadata [ 'rhs_module' ] = $this->definition [ 'rhs_module' ] ;
$relMetadata [ 'rhs_table' ] = $this->getTablename($this->definition [ 'rhs_module' ]) ;
$relMetadata ['rhs_key'] = 'parent_id';
$relMetadata ['relationship_type'] = 'one-to-many';
$relMetadata ['relationship_role_column'] = 'parent_type';
$relMetadata ['relationship_role_column_value'] = $this->definition [ 'lhs_module' ] ;
return array( $this->lhs_module => array(
'relationships' => array ($relationshipName => $relMetadata),
'fields' => '', 'indices' => '', 'table' => '')
) ;
}
/*
* Shortcut to construct an Activities collection subpanel
* @param AbstractRelationship $relationship Source relationship to Activities module
*/
protected function buildActivitiesSubpanelDefinition ( $relationshipName )
{
return array (
'order' => 10 ,
'sort_order' => 'desc' ,
'sort_by' => 'date_start' ,
'title_key' => 'LBL_ACTIVITIES_SUBPANEL_TITLE' ,
'type' => 'collection' ,
'subpanel_name' => 'activities' , //this value is not associated with a physical file
'module' => 'Activities' ,
'top_buttons' => array (
array ( 'widget_class' => 'SubPanelTopCreateTaskButton' ) ,
array ( 'widget_class' => 'SubPanelTopScheduleMeetingButton' ) ,
array ( 'widget_class' => 'SubPanelTopScheduleCallButton' ) ,
array ( 'widget_class' => 'SubPanelTopComposeEmailButton' ) ) ,
'collection_list' => array (
'meetings' => array (
'module' => 'Meetings' ,
'subpanel_name' => 'ForActivities' ,
'get_subpanel_data' => $relationshipName. '_meetings' ) ,
'tasks' => array (
'module' => 'Tasks' ,
'subpanel_name' => 'ForActivities' ,
'get_subpanel_data' => $relationshipName. '_tasks' ) ,
'calls' => array (
'module' => 'Calls' ,
'subpanel_name' => 'ForActivities' ,
'get_subpanel_data' => $relationshipName. '_calls' ) ) ) ;
}
/*
* Shortcut to construct a History collection subpanel
* @param AbstractRelationship $relationship Source relationship to Activities module
*/
protected function buildHistorySubpanelDefinition ( $relationshipName )
{
return array (
'order' => 20 ,
'sort_order' => 'desc' ,
'sort_by' => 'date_modified' ,
'title_key' => 'LBL_HISTORY' ,
'type' => 'collection' ,
'subpanel_name' => 'history' , //this values is not associated with a physical file.
'module' => 'History' ,
'top_buttons' => array (
array ( 'widget_class' => 'SubPanelTopCreateNoteButton' ) ,
array ( 'widget_class' => 'SubPanelTopArchiveEmailButton'),
array ( 'widget_class' => 'SubPanelTopSummaryButton' ) ) ,
'collection_list' => array (
'meetings' => array (
'module' => 'Meetings' ,
'subpanel_name' => 'ForHistory' ,
'get_subpanel_data' => $relationshipName. '_meetings' ) ,
'tasks' => array (
'module' => 'Tasks' ,
'subpanel_name' => 'ForHistory' ,
'get_subpanel_data' => $relationshipName. '_tasks' ) ,
'calls' => array (
'module' => 'Calls' ,
'subpanel_name' => 'ForHistory' ,
'get_subpanel_data' => $relationshipName. '_calls' ) ,
'notes' => array (
'module' => 'Notes' ,
'subpanel_name' => 'ForHistory' ,
'get_subpanel_data' => $relationshipName. '_notes' ) ,
'emails' => array (
'module' => 'Emails' ,
'subpanel_name' => 'ForHistory' ,
'get_subpanel_data' => $relationshipName. '_emails' ) ) ) ;
}
}
?>

View File

@@ -0,0 +1,382 @@
<?php
if (! defined ( 'sugarEntry' ) || ! sugarEntry)
die ( 'Not A Valid Entry Point' ) ;
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
require_once 'modules/ModuleBuilder/parsers/relationships/AbstractRelationships.php' ;
require_once 'modules/ModuleBuilder/parsers/relationships/RelationshipsInterface.php' ;
require_once 'modules/ModuleBuilder/parsers/relationships/RelationshipFactory.php' ;
class DeployedRelationships extends AbstractRelationships implements RelationshipsInterface
{
function __construct ($moduleName)
{
$this->moduleName = $moduleName ;
$this->load () ;
}
static function findRelatableModules ()
{
return parent::findRelatableModules ( true ) ;
}
/*
* Load the set of relationships for this module - the set is the combination of that held in the working file plus all of the relevant deployed relationships for the module
* Note that deployed relationships are readonly and cannot be modified - getDeployedRelationships() takes care of marking them as such
* Assumes that only called for modules which exist in $beansList - otherwise get_module_info will break
* This means that load() cannot be called for Activities, only Tasks, Notes, etc
*
* Note that we may need to adjust the cardinality for any custom relationships that we do not have entries for in the working directory
* These relationships might have been loaded from an installation package by ModuleInstaller, or the custom/working directory might have been cleared at some point
* The cardinality in the installed relationship is not necessarily correct for custom relationships, which currently are all built as many-to-many relationships
* Instead we must obtain the true cardinality from a property we added to the relationship metadata when we created the relationship
* This relationship metadata is accessed through the Table Dictionary
*/
function load ()
{
$relationships = $this->getDeployedRelationships () ;
if (! empty ( $relationships ))
{
// load the relationship definitions for all installed custom relationships into $dictionary
$dictionary = array ( ) ;
if (file_exists ( 'custom/application/Ext/TableDictionary/tabledictionary.ext.php' ))
{
include ('custom/application/Ext/TableDictionary/tabledictionary.ext.php') ;
}
$invalidModules = array ( 'Users' ) ;
$validModules = array_keys ( self::findRelatableModules () ) ;
// now convert the relationships array into an array of AbstractRelationship objects
foreach ( $relationships as $name => $definition )
{
if (($definition [ 'lhs_module' ] == $this->moduleName) || ($definition [ 'rhs_module' ] == $this->moduleName))
{
if (in_array ( $definition [ 'lhs_module' ], $validModules ) && in_array ( $definition [ 'rhs_module' ], $validModules ) && ! in_array ( $definition [ 'lhs_module' ], $invalidModules ) && ! in_array ( $definition [ 'rhs_module' ], $invalidModules ))
{
// identify the subpanels for this relationship - TODO: optimize this - currently does m x n scans through the subpanel list...
$definition [ 'rhs_subpanel' ] = self::identifySubpanel ( $definition [ 'lhs_module' ], $definition [ 'rhs_module' ] ) ;
$definition [ 'lhs_subpanel' ] = self::identifySubpanel ( $definition [ 'rhs_module' ], $definition [ 'lhs_module' ] ) ;
// now adjust the cardinality with the true cardinality found in the relationships metadata (see method comment above)
if (! empty ( $dictionary ) && ! empty ( $dictionary [ $name ] ) ) {
if (! empty ( $dictionary [ $name ] [ 'true_relationship_type' ] )) {
$definition [ 'relationship_type' ] = $dictionary [ $name ] [ 'true_relationship_type' ] ;
}
if (! empty ( $dictionary [ $name ] [ 'from_studio' ] )) {
$definition [ 'from_studio' ] = $dictionary [ $name ] [ 'from_studio' ] ;
}
$definition [ 'is_custom' ] = true;
}
$this->relationships [ $name ] = RelationshipFactory::newRelationship ( $definition ) ;
}
}
}
}
/* // Now override with any definitions from the working directory
// must do this to capture one-to-ones that we have created as these don't show up in the relationship table that is the source for getDeployedRelationships()
$overrides = parent::_load ( "custom/working/modules/{$this->moduleName}" ) ;
foreach ( $overrides as $name => $relationship )
{
$this->relationships [ $name ] = $relationship ;
}*/
}
/*
* Save this modules relationship definitions out to a working file
*/
function save ()
{
parent::_save ( $this->relationships, "custom/working/modules/{$this->moduleName}" ) ;
}
/*
* Update pre-5.1 relationships to the 5.1 relationship definition
* There is nothing to do for Deployed relationships as these were only introduced in 5.1
* @param array definition The 5.0 relationship definition
* @return array The definition updated to 5.1 format
*/
protected function _updateRelationshipDefinition ($definition)
{
return $definition ;
}
/*
* Use the module Loader to delete the relationship from the instance.
*/
function delete ($rel_name)
{
require_once("ModuleInstall/ModuleInstaller.php");
require_once ('modules/Administration/QuickRepairAndRebuild.php') ;
$mi = new ModuleInstaller();
$mi->silent = true;
$mi->uninstall_relationship("custom/metadata/{$rel_name}MetaData.php");
// now clear all caches so that our changes are visible
Relationship::delete_cache();
$mi->rebuild_tabledictionary();
$MBmodStrings = $GLOBALS [ 'mod_strings' ];
$GLOBALS [ 'mod_strings' ] = return_module_language ( '', 'Administration' ) ;
$rac = new RepairAndClear ( ) ;
$rac->repairAndClearAll ( array ( 'clearAll', 'rebuildExtensions', ), array ( $GLOBALS [ 'mod_strings' ] [ 'LBL_ALL_MODULES' ] ), true, false ) ;
$GLOBALS [ 'mod_strings' ] = $MBmodStrings;
}
/*
* Return the set of all known relevant relationships for a deployed module
* The set is made up of the relationships held in this class, plus all those already deployed in the application
* @return array Set of all relevant relationships
*/
protected function getAllRelationships ()
{
return array_merge ( $this->relationships, parent::getDeployedRelationships () ) ;
}
/*
* Return the name of the first (currently only) subpanel displayed in the DetailView of $thisModuleName provided by $sourceModuleName
* We can assume that both sides of the relationship are deployed modules as this is only called within the context of DeployedRelationships
* @param string $thisModuleName Name of the related module
* @param string $sourceModuleName Name of the primary module
* @return string Name of the subpanel if found; null otherwise
*/
static private function identifySubpanel ($thisModuleName , $sourceModuleName)
{
$module = get_module_info ( $thisModuleName ) ;
require_once ('include/SubPanel/SubPanelDefinitions.php') ;
$spd = new SubPanelDefinitions ( $module ) ;
$subpanelNames = $spd->get_available_tabs () ; // actually these are the displayed subpanels
$subPanels = array ( ) ;
foreach ( $subpanelNames as $key => $name )
{
$GLOBALS [ 'log' ]->debug ( $thisModuleName . " " . $name ) ;
$subPanel = $spd->load_subpanel ( $name ) ;
if (! isset ( $subPanel->_instance_properties [ 'collection_list' ] ))
{
if ($sourceModuleName == $subPanel->_instance_properties [ 'module' ])
{
return $subPanel->_instance_properties [ 'subpanel_name' ] ;
}
}
}
return null ;
}
/*
* Return the name of the first (currently only) relate field of $thisModuleName sourced from by $sourceModuleName
* We can assume that both sides of the relationship are deployed modules as this is only called within the context of DeployedRelationships
* @param string $thisModuleName Name of the related module
* @param string $sourceModuleName Name of the primary module
* @return string Name of the relate field, if found; null otherwise
*/
static private function identifyRelateField ($thisModuleName , $sourceModuleName)
{
$module = get_module_info ( $thisModuleName ) ;
foreach ( $module->field_defs as $field )
{
if ($field [ 'type' ] == 'relate' && isset ( $field [ 'module' ] ) && $field [ 'module' ] == $sourceModuleName)
return $field [ 'name' ] ;
}
return null ;
}
/*
* As of SugarCRM 5.1 the subpanel code and the widgets have difficulty handling multiple subpanels or relate fields from the same module
* Until this is fixed, we new relationships which will trigger this problem must be flagged as "relationship_only" and built without a UI component
* This function is called from the view when constructing a new relationship
* We can assume that both sides of the relationship are deployed modules as this is only called within the context of DeployedRelationships
* @param AbstractRelationship $relationship The relationship to be enforced
*/
public function enforceRelationshipOnly ($relationship)
{
$lhs = $relationship->lhs_module ;
$rhs = $relationship->rhs_module ;
// if the lhs_module already has a subpanel or relate field sourced from the rhs_module,
// or the rhs_module already has a subpanel or relate field sourced from the lhs_module,
// then set "relationship_only" in the relationship
// if (($relationship->getType() != MB_ONETOONE && ! is_null ( self::identifySubpanel ( $lhs, $rhs ) )) || ($relationship->getType() == MB_MANYTOMANY && ! is_null ( self::identifySubpanel ( $rhs, $lhs ) )) || ($relationship->getType() == MB_ONETOONE && ! is_null ( self::identifyRelateField ( $rhs, $lhs ) )) || ($relationship->getType() != MB_MANYTOMANY && ! is_null ( self::identifyRelateField ( $lhs, $rhs ) )))
// $relationship->setRelationship_only () ;
}
/*
* BUILD FUNCTIONS
*/
/*
* Implement all of the Relationships in this set of relationships
* This is more general than it needs to be given that deployed relationships are built immediately - there should only be one relationship to build here...
* We use the Extension mechanism to do this for DeployedRelationships
* All metadata is placed in the modules Ext directory, and then Rebuild is called to activate them
*/
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" ) ;
}
/*
* Add any fields to the DetailView and EditView of the appropriate modules
* @param string $basepath Basepath location for this module (not used)
* @param string $relationshipName Name of this relationship (for uniqueness)
* @param array $layoutAdditions An array of module => fieldname
* return null
*/
protected function saveFieldsToLayouts ($basepath , $dummy , $relationshipName , $layoutAdditions)
{
require_once 'modules/ModuleBuilder/parsers/views/GridLayoutMetaDataParser.php' ;
// 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
// would be better if GridLayoutMetaDataParser could handle this gracefully, so we don't have to maintain this list here
$invalidModules = array ( 'emails' , 'kbdocuments' ) ;
foreach ( $layoutAdditions as $deployedModuleName => $fieldName )
{
if (! in_array ( strtolower ( $deployedModuleName ), $invalidModules ))
foreach ( array ( MB_EDITVIEW , MB_DETAILVIEW ) as $view )
{
$GLOBALS [ 'log' ]->info ( get_class ( $this ) . ": adding $fieldName to $view layout for module $deployedModuleName" ) ;
$parser = new GridLayoutMetaDataParser ( $view, $deployedModuleName ) ;
$parser->addField ( array ( 'name' => $fieldName ) ) ;
$parser->handleSave ( false ) ;
}
}
}
}
?>

View File

@@ -0,0 +1,105 @@
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
require_once 'modules/ModuleBuilder/parsers/relationships/AbstractRelationship.php' ;
/*
* Class to manage the metadata for a Many-To-Many Relationship
* The LHS (One) module will receive a new subpanel for the RHS module
* The RHS (Many) module will receive a new subpanel for the RHS module
* The subpanels get their data ('get_subpanel_data') from two link fields (one each) that reference a new Relationship
*
* In OOB modules it's done the same way (e.g. cases_bugs)
*/
class ManyToManyRelationship extends AbstractRelationship
{
/*
* Constructor
* @param array $definition Parameters passed in as array with keys defined in parent::keys
*/
function __construct ($definition)
{
parent::__construct ( $definition ) ;
}
/*
* BUILD methods called during the build
*/
/*
* Construct subpanel definitions
* The format is that of TO_MODULE => relationship, FROM_MODULE, FROM_MODULES_SUBPANEL, mimicking the format in the layoutdefs.php
* @return array An array of subpanel definitions, keyed by module
*/
function buildSubpanelDefinitions ()
{
$subpanelDefinitions = array ( ) ;
if (!$this->relationship_only)
{
$subpanelDefinitions [ $this->rhs_module ] = $this->getSubpanelDefinition ( $this->relationship_name, $this->lhs_module, $this->lhs_subpanel, $this->getLeftModuleSystemLabel() ) ;
$subpanelDefinitions [ $this->lhs_module ] = $this->getSubpanelDefinition ( $this->relationship_name, $this->rhs_module, $this->rhs_subpanel, $this->getRightModuleSystemLabel() ) ;
}
return $subpanelDefinitions ;
}
/*
* @return array An array of field definitions, ready for the vardefs, keyed by module
*/
function buildVardefs ( )
{
$vardefs = array ( ) ;
$vardefs [ $this->rhs_module ] [] = $this->getLinkFieldDefinition ( $this->lhs_module, $this->relationship_name, false,
'LBL_' . strtoupper ( $this->relationship_name . '_FROM_' . $this->getLeftModuleSystemLabel() ) . '_TITLE' ) ;
$vardefs [ $this->lhs_module ] [] = $this->getLinkFieldDefinition ( $this->rhs_module, $this->relationship_name, false,
'LBL_' . strtoupper ( $this->relationship_name . '_FROM_' . $this->getRightModuleSystemLabel() ) . '_TITLE' ) ;
return $vardefs ;
}
/*
* @return array An array of relationship metadata definitions
*/
function buildRelationshipMetaData ()
{
return array( $this->lhs_module => $this->getRelationshipMetaData ( MB_MANYTOMANY ) ) ;
}
}

View File

@@ -0,0 +1,149 @@
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
require_once 'modules/ModuleBuilder/parsers/relationships/AbstractRelationship.php' ;
require_once 'modules/ModuleBuilder/parsers/relationships/OneToManyRelationship.php' ;
require_once 'modules/ModuleBuilder/parsers/constants.php' ;
/*
* Class to manage the metadata for a many-To-one Relationship
* Exactly the same as a one-to-many relationship except lhs and rhs modules have been reversed.
*/
class ManyToOneRelationship extends AbstractRelationship
{
/*
* Constructor
* @param array $definition Parameters passed in as array defined in parent::$definitionKeys
* The lhs_module value is for the One side; the rhs_module value is for the Many
*/
function __construct ($definition)
{
parent::__construct ( $definition ) ;
$onetomanyDef = array_merge($definition, array(
'rhs_label' => isset($definition['lhs_label']) ? $definition['lhs_label'] : null,
'lhs_label' => isset($definition['rhs_label']) ? $definition['rhs_label'] : null,
'lhs_subpanel' => isset($definition['rhs_subpanel']) ? $definition['rhs_subpanel'] : null,
'rhs_subpanel' => isset($definition['lhs_subpanel']) ? $definition['lhs_subpanel'] : null,
'lhs_module' => isset($definition['rhs_module']) ? $definition['rhs_module'] : null,
'lhs_table' => isset($definition['rhs_table']) ? $definition['rhs_table'] : null,
'lhs_key' => isset($definition['rhs_key']) ? $definition['rhs_key'] : null,
'rhs_module' => isset($definition['lhs_module']) ? $definition['lhs_module'] : null,
'rhs_table' => isset($definition['lhs_table']) ? $definition['lhs_table'] : null,
'rhs_key' => isset($definition['lhs_key']) ? $definition['lhs_key'] : null,
'join_key_lhs' => isset($definition['join_key_rhs']) ? $definition['join_key_rhs'] : null,
'join_key_rhs' => isset($definition['join_key_lhs']) ? $definition['join_key_lhs'] : null,
'relationship_type' => MB_ONETOMANY,
));
$this->one_to_many = new OneToManyRelationship($onetomanyDef);
}
/*
* BUILD methods called during the build
*/
function buildLabels ()
{
return $this->one_to_many->buildLabels();
}
/*
* Construct subpanel definitions
* The format is that of TO_MODULE => relationship, FROM_MODULE, FROM_MODULES_SUBPANEL, mimicking the format in the layoutdefs.php
* @return array An array of subpanel definitions, keyed by the module
*/
function buildSubpanelDefinitions ()
{
return $this->one_to_many->buildSubpanelDefinitions();
}
/*
* @return array An array of field definitions, ready for the vardefs, keyed by module
*/
function buildVardefs ( )
{
return $this->one_to_many->buildVardefs();
}
/*
* Define what fields to add to which modules layouts
* @return array An array of module => fieldname
*/
function buildFieldsToLayouts ()
{
if ($this->relationship_only)
return array () ;
return array( $this->lhs_module => $this->relationship_name . "_name" ) ; // this must match the name of the relate field from buildVardefs
}
/*
* @return array An array of relationship metadata definitions
*/
function buildRelationshipMetaData ()
{
return $this->one_to_many->buildRelationshipMetaData();
}
public function setName ($relationshipName)
{
parent::setName($relationshipName);
$this->one_to_many->setname($relationshipName);
}
public function setReadonly ()
{
parent::setReadonly();
$this->one_to_many->setReadonly();
}
public function delete ()
{
parent::delete();
$this->one_to_many->delete();
}
public function setRelationship_only ()
{
parent::setRelationship_only();
$this->one_to_many->setRelationship_only();
}
}
?>

View File

@@ -0,0 +1,153 @@
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
require_once 'modules/ModuleBuilder/parsers/relationships/AbstractRelationship.php' ;
/*
* Class to manage the metadata for a One-To-Many Relationship
* The One-To-Many relationships created by this class are a combination of a subpanel and a custom relate field
* The LHS (One) module will receive a new subpanel for the RHS module. The subpanel gets its data ('get_subpanel_data') from a link field that references a new Relationship
* The RHS (Many) module will receive a new relate field to point back to the LHS
*
* OOB modules implement One-To-Many relationships as:
*
* On the LHS (One) side:
* A Relationship of type one-to-many in the rhs modules vardefs.php
* A link field in the same vardefs.php with 'relationship'= the relationship name and 'source'='non-db'
* A subpanel which gets its data (get_subpanel_data) from the link field
*
* On the RHS (Many) side:
* A Relate field in the vardefs, formatted as in this example, which references a link field:
* 'name' => 'account_name',
* 'rname' => 'name',
* 'id_name' => 'account_id',
* 'vname' => 'LBL_ACCOUNT_NAME',
* 'join_name'=>'accounts',
* 'type' => 'relate',
* 'link' => 'accounts',
* 'table' => 'accounts',
* 'module' => 'Accounts',
* 'source' => 'non-db'
* A link field which references the shared Relationship
*/
class OneToManyRelationship extends AbstractRelationship
{
/*
* Constructor
* @param array $definition Parameters passed in as array defined in parent::$definitionKeys
* The lhs_module value is for the One side; the rhs_module value is for the Many
*/
function __construct ($definition)
{
parent::__construct ( $definition ) ;
}
/*
* BUILD methods called during the build
*/
/*
* Construct subpanel definitions
* The format is that of TO_MODULE => relationship, FROM_MODULE, FROM_MODULES_SUBPANEL, mimicking the format in the layoutdefs.php
* @return array An array of subpanel definitions, keyed by the module
*/
function buildSubpanelDefinitions ()
{
if ($this->relationship_only)
return array () ;
$source = "";
if ($this->rhs_module == $this->lhs_module)
{
$source = $this->getJoinKeyLHS();
}
return array(
$this->lhs_module => $this->getSubpanelDefinition (
$this->relationship_name, $this->rhs_module, $this->rhs_subpanel , $this->getRightModuleSystemLabel() , $source
)
);
}
/*
* @return array An array of field definitions, ready for the vardefs, keyed by module
*/
function buildVardefs ( )
{
$vardefs = array ( ) ;
$vardefs [ $this->rhs_module ] [] = $this->getLinkFieldDefinition ( $this->lhs_module, $this->relationship_name, false,
'LBL_' . strtoupper ( $this->relationship_name . '_FROM_' . $this->getLeftModuleSystemLabel() ) . '_TITLE') ;
if ($this->rhs_module != $this->lhs_module )
{
$vardefs [ $this->lhs_module ] [] = $this->getLinkFieldDefinition ( $this->rhs_module, $this->relationship_name, true,
'LBL_' . strtoupper ( $this->relationship_name . '_FROM_' . $this->getRightModuleSystemLabel() ) . '_TITLE');
}
if (! $this->relationship_only)
{
$vardefs [ $this->rhs_module ] [] = $this->getRelateFieldDefinition ( $this->lhs_module, $this->relationship_name, $this->getLeftModuleSystemLabel() ) ;
$vardefs [ $this->rhs_module ] [] = $this->getLink2FieldDefinition ( $this->lhs_module, $this->relationship_name, true,
'LBL_' . strtoupper ( $this->relationship_name . '_FROM_' . $this->getRightModuleSystemLabel() ) . '_TITLE');
}
return $vardefs ;
}
/*
* Define what fields to add to which modules layouts
* @return array An array of module => fieldname
*/
function buildFieldsToLayouts ()
{
if ($this->relationship_only)
return array () ;
return array( $this->rhs_module => $this->relationship_name . "_name" ) ; // this must match the name of the relate field from buildVardefs
}
/*
* @return array An array of relationship metadata definitions
*/
function buildRelationshipMetaData ()
{
return array( $this->lhs_module => $this->getRelationshipMetaData ( MB_ONETOMANY ) ) ;
}
}
?>

View File

@@ -0,0 +1,128 @@
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
require_once 'modules/ModuleBuilder/parsers/relationships/AbstractRelationship.php' ;
/*
* Class to manage the metadata for a One-To-One Relationship
* The LHS module will receive a new relate field to point back to the RHS
* The RHS module will receive a new relate field to point back to the LHS
*
* OOB modules implement One-To-One relationships as:
* A Relationship of type one-to-one in one modules vardefs.php
* A single link field in the same vardefs.php with 'relationship'= the relationship name, and 'link-type'='one', 'Module'=other side, and 'source'='non-db'
* These are not common - examples are in InboundEmail and Schedulers, both pre-5.0 modules
* InboundEmail:
* 'created_by_link' => array (
'name' => 'created_by_link',
'type' => 'link',
'relationship' => 'inbound_email_created_by',
'vname' => 'LBL_CREATED_BY_USER',
'link_type' => 'one',
'module' => 'Users',
'bean_name' => 'User',
'source' => 'non-db',
),
*/
class OneToOneRelationship extends AbstractRelationship
{
/*
* Constructor
* @param array $definition Parameters passed in as array with keys defined in parent::keys
*/
function __construct ($definition)
{
parent::__construct ( $definition ) ;
}
/*
* BUILD methods called during the build
*/
/*
* @return array An array of relationship metadata definitions
*/
function buildRelationshipMetaData ()
{
return array( $this->lhs_module => $this->getRelationshipMetaData ( MB_ONETOONE ) ) ;
}
/* Build a set of Link Field definitions for this relationship
* @return array An array of field definitions, ready for the vardefs, keyed by module
*/
function buildVardefs ( )
{
$vardefs = array ( ) ;
$vardefs [ $this->rhs_module ] [] = $this->getLinkFieldDefinition ( $this->lhs_module, $this->relationship_name , false,
'LBL_' . strtoupper ( $this->relationship_name . '_FROM_' . $this->getLeftModuleSystemLabel() ) . '_TITLE' ) ;
$vardefs [ $this->lhs_module ] [] = $this->getLinkFieldDefinition ( $this->rhs_module, $this->relationship_name, false,
'LBL_' . strtoupper ( $this->relationship_name . '_FROM_' . $this->getRightModuleSystemLabel() ) . '_TITLE' ) ;
if (!$this->relationship_only)
{
$vardefs [ $this->lhs_module ] [] = $this->getRelateFieldDefinition ( $this->rhs_module, $this->relationship_name, $this->getRightModuleSystemLabel() ) ;
$vardefs [ $this->rhs_module ] [] = $this->getRelateFieldDefinition ( $this->lhs_module, $this->relationship_name, $this->getLeftModuleSystemLabel() ) ;
$vardefs [ $this->lhs_module ] [] = $this->getLink2FieldDefinition ( $this->rhs_module, $this->relationship_name , false,
'LBL_' . strtoupper ( $this->relationship_name . '_FROM_' . $this->getRightModuleSystemLabel() ) . '_TITLE' ) ;
$vardefs [ $this->rhs_module ] [] = $this->getLink2FieldDefinition ( $this->lhs_module, $this->relationship_name , false,
'LBL_' . strtoupper ( $this->relationship_name . '_FROM_' . $this->getLeftModuleSystemLabel() ) . '_TITLE' ) ;
}
return $vardefs ;
}
/*
* Define what fields to add to which modules layouts
* @return array An array of module => fieldname
*/
function buildFieldsToLayouts ()
{
if ($this->relationship_only)
return array () ;
if ($this->lhs_module == $this->rhs_module) // don't add in two fields on recursive relationships
return array ( $this->lhs_module => $this->relationship_name . "_name" );
else
return array ( $this->lhs_module => $this->relationship_name . "_name" , $this->rhs_module => $this->relationship_name . "_name" ) ;
}
}
?>

View File

@@ -0,0 +1,81 @@
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
require_once 'modules/ModuleBuilder/parsers/constants.php' ;
class RelationshipFactory
{
/*
* Construct a new relationship of type as provided by the $definition
* @param array $definition Complete definition of the relationship, as specified by AbstractRelationship::keys
*/
static function newRelationship ($definition = array())
{
// handle the case where a relationship_type is not provided - set it to Many-To-Many as this was the usual type in ModuleBuilder
if (! isset ( $definition [ 'relationship_type' ] ))
$definition [ 'relationship_type' ] = MB_MANYTOMANY ;
if (!empty ($definition['for_activities']) && $definition['for_activities'] == true) {
require_once 'modules/ModuleBuilder/parsers/relationships/ActivitiesRelationship.php';
return new ActivitiesRelationship ($definition);
}
switch ( strtolower ( $definition [ 'relationship_type' ] ))
{
case strtolower ( MB_ONETOONE ) :
require_once 'modules/ModuleBuilder/parsers/relationships/OneToOneRelationship.php' ;
return new OneToOneRelationship ( $definition ) ;
case strtolower ( MB_ONETOMANY ) :
require_once 'modules/ModuleBuilder/parsers/relationships/OneToManyRelationship.php' ;
return new OneToManyRelationship ( $definition ) ;
case strtolower ( MB_MANYTOONE ) :
require_once 'modules/ModuleBuilder/parsers/relationships/ManyToOneRelationship.php' ;
return new ManyToOneRelationship ( $definition ) ;
// default case is Many-To-Many as this was the only type ModuleBuilder could create and so much of the MB code assumes Many-To-Many
default :
$definition [ 'relationship_type' ] = MB_MANYTOMANY ;
require_once 'modules/ModuleBuilder/parsers/relationships/ManyToManyRelationship.php' ;
return new ManyToManyRelationship ( $definition ) ;
}
}
}
?>

View File

@@ -0,0 +1,55 @@
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
interface RelationshipsInterface
{
static public function findRelatableModules () ;
public function load () ;
// public function build () ;
public function getRelationshipList ();
public function get ($relationshipName) ;
public function add ($relationship) ;
// public function delete ($relationshipName) ;
}

View File

@@ -0,0 +1,415 @@
<?php
if (! defined ( 'sugarEntry' ) || ! sugarEntry)
die ( 'Not A Valid Entry Point' ) ;
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
require_once 'modules/ModuleBuilder/parsers/relationships/AbstractRelationships.php' ;
require_once 'modules/ModuleBuilder/parsers/relationships/RelationshipsInterface.php' ;
require_once 'modules/ModuleBuilder/parsers/relationships/RelationshipFactory.php' ;
class UndeployedRelationships extends AbstractRelationships implements RelationshipsInterface
{
protected $basepath ; // Base directory for the lhs_module
protected $packageName ;
private $activitiesToAdd ; // if we need to add in the composite Activities and History subpanels to the module during the build
/*
* Constructor
* Automatically loads in any saved relationships
* @param string $path The pathname of the base module directory
*/
function __construct ($path)
{
$this->basepath = $path ;
// pull the module and package names out of the path
$this->moduleName = basename ( $path, "/" ) ; // just in case there are any trailing /
$this->packageName = basename ( dirname ( dirname ( $path ) ) ) ; // simpler than explode :)
require_once 'modules/ModuleBuilder/MB/ModuleBuilder.php' ;
$mb = new ModuleBuilder ( ) ;
$this->packageKey = $mb->getPackageKey ( $this->packageName ) ;
$this->load () ;
}
/*
* Find all modules, deployed and undeployed, that can participate in a relationship
* @return array Array of [$module][$subpanel]
*/
static function findRelatableModules ()
{
// first find all deployed modules that we might participate in a relationship
$relatableModules = parent::findRelatableModules ( ) ;
// now add in the undeployed modules - those in custom/modulebuilder
// note that if a module exists in both deployed and undeployed forms, the subpanels from the undeployed form are used...
require_once 'modules/ModuleBuilder/MB/ModuleBuilder.php' ;
$mb = new ModuleBuilder ( ) ;
$mb->getPackages () ;
foreach ( $mb->getPackageList () as $packageName )
{
$package = $mb->packages [ $packageName ] ;
foreach ( $package->modules as $module )
{
$relatableModules [ $package->key . "_" . $module->name ] = $module->getProvidedSubpanels () ;
}
}
return $relatableModules ;
}
/*
* Add a relationship to the set
* For undeployed relationships we always make the fields in the relationship visible in the layouts now, rather than waiting until build time, so
* that the admin may move them around or otherwise edit them before the module is deployed
* @param AbstractRelationship $relationship The relationship to add
*/
function add ($relationship)
{
parent::add ( $relationship ) ;
$this->addFieldsToUndeployedLayouts ( $relationship ) ; // must come after parent::add as we need the relationship_name in the relationships getFieldsToLayouts() which is called by addFieldsToUndeployedLayouts()
}
/*
* Delete a relationship by name
* In future, if we need to actually track deleted relationships then just call $relationship->delete() instead
* @param string $relationshipName The unique name for this relationship, as returned by $relationship->getName()
*/
function delete ($relationshipName)
{
if ($relationship = $this->get ( $relationshipName ))
{
$this->removeFieldsFromUndeployedLayouts ( $relationship ) ;
unset ( $this->relationships [ $relationshipName ] ) ;
}
}
/*
* Load the saved relationship definitions for this module
*/
function load ()
{
$this->relationships = parent::_load ( $this->basepath ) ;
}
/*
* Save this modules relationship definitions out to a working file
*/
function save ()
{
parent::_save ( $this->relationships, $this->basepath ) ;
}
/*
* Update pre-5.1 relationships to the 5.1 relationship definition
* @param array definition The 5.0 relationship definition
* @return array The definition updated to 5.1 format
*/
protected function _updateRelationshipDefinition ($definition)
{
if (isset ( $definition [ 'relate' ] ))
{
$newDefinition = array ( ) ;
foreach ( array ( 'relate' => 'rhs_module' , 'rsub' => 'rhs_subpanel' , 'msub' => 'lhs_subpanel' , 'label' => 'label' ) as $oldParameter => $newParameter )
{
if (isset ( $definition [ $oldParameter ] ))
{
$definition [ $newParameter ] = $definition [ $oldParameter ] ;
unset ( $definition [ $oldParameter ] ) ;
}
}
$definition [ 'lhs_module' ] = "{$this->packageKey}_{$this->moduleName}" ;
// finally update the relationship name
unset ( $definition [ 'name' ] ) ; // clear the oldstyle name
}
return $definition ;
}
/*
* Implementation of getAllRelationships() for Undeployed modules
* 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
* @return array Set of all relevant relationships
*/
protected function getAllRelationships ()
{
// start with the set of relationships known to this module plus those already deployed
$allRelationships = array_merge ( $this->relationships, parent::getDeployedRelationships () ) ;
// add in the relationships known to ModuleBuilder
require_once 'modules/ModuleBuilder/MB/ModuleBuilder.php' ;
$mb = new ModuleBuilder ( ) ;
$mb->getPackages () ;
foreach ( $mb->getPackageList () as $packageName )
{
$package = $mb->packages [ $packageName ] ;
foreach ( $package->modules as $module )
{
foreach ( $module->relationships->getRelationshipList () as $relationshipName )
{
$relationship = $module->relationships->get ( $relationshipName ) ;
$allRelationships [ $relationship->getName () ] = $relationship->getDefinition () ;
}
}
}
return $allRelationships ;
}
/*
* As of SugarCRM 5.1 the subpanel code and the widgets have difficulty handling multiple subpanels or relate fields from the same module
* Until this is fixed, we new relationships which will trigger this problem must be flagged as "relationship_only" and built without a UI component
* This function is called from the view when constructing a new relationship
* @param AbstractRelationship $relationship The relationship to be enforced
*/
public function enforceRelationshipOnly ($relationship)
{
// if we already have a relationship between this lhs_module and this rhs_module then set RelationshipOnly flag
foreach ( $this->relationships as $rel )
{
if ($rel->lhs_module == $relationship->lhs_module && $rel->rhs_module == $relationship->rhs_module)
{
$rel->setRelationship_only () ;
break ;
}
}
}
/*
* BUILD FUNCTIONS
*/
/*
* Translate the set of relationship objects into files that the Module Loader can work with
* @param $basepath string Pathname of the directory to contain the build
*/
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 ) ;
}
/*
* Translate a set of subpanelDefinitions into files for the Module Loader
* @param string $basepath Basepath location for this module
* @param $installDefPrefix Pathname prefix for the installdefs, for example for ModuleBuilder use "<basepath>/SugarModules"
* @param array $subpanelDefinitions Set of subpanel definitions in the form $subpanelDefinitions[$for_module][]
* @param string $relationshipName The name of the relationship for this subpanel definition
* @return array $installDefs Set of new installDefs
*/
protected function saveSubpanelDefinitions ($basepath , $installDefPrefix , $relationshipName , $subpanelDefinitions)
{
mkdir_recursive ( "$basepath/layoutdefs/" ) ;
foreach ( $subpanelDefinitions as $moduleName => $definitions )
{
$filename = "$basepath/layoutdefs/{$relationshipName}_{$moduleName}.php" ;
foreach ( $definitions as $definition )
{
$GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->saveSubpanelDefinitions(): saving the following to {$filename}" . print_r ( $definition, true ) ) ;
if (empty($definition ['get_subpanel_data']) || $definition ['subpanel_name'] == 'history' || $definition ['subpanel_name'] == 'activities') {
$definition ['get_subpanel_data'] = $definition ['subpanel_name'];
}
write_array_to_file ( 'layout_defs["' . $moduleName . '"]["subpanel_setup"]["' . strtolower ( $definition [ 'get_subpanel_data' ] ) . '"]', $definition, $filename, "a" ) ;
}
$installDefs [ $moduleName ] = array ( 'from' => "{$installDefPrefix}/relationships/layoutdefs/{$relationshipName}_{$moduleName}.php" , 'to_module' => $moduleName ) ;
}
return $installDefs ;
}
/*
* Add the installDefs for this relationship to the definitions in the parameter
* Required by MBModule
* @param reference installDef Reference to the set of installDefs to which this relationship's installDefs should be added
*/
function addInstallDefs (&$installDef)
{
foreach ( $this->installDefs as $name => $def )
{
if (! empty ( $def ))
{
foreach ( $def as $val )
{
$installDef [ $name ] [] = $val ;
}
}
}
}
private function addFieldsToUndeployedLayouts ($relationship)
{
return $this->updateUndeployedLayout ( $relationship, true ) ;
}
private function removeFieldsFromUndeployedLayouts ($relationship)
{
return $this->updateUndeployedLayout ( $relationship, false ) ;
}
/*
* Add any relate fields to the DetailView and EditView of the appropriate module immediately (don't wait for a build)
* @param AbstractRelationship $relationship The relationship whose fields we are to add or remove
* @param boolean $actionAdd True if we are to add; false if to remove
* return null
*/
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 ;
}
/*
* Add any fields to the DetailView and EditView of the appropriate modules
* Only add into deployed modules, as addFieldsToUndeployedLayouts has done this already for undeployed modules (and the admin might have edited the layouts already)
* @param string $basepath Basepath location for this module (not used)
* @param string $relationshipName Name of this relationship (for uniqueness)
* @param array $layoutAdditions An array of module => fieldname
* return null
*/
protected function saveFieldsToLayouts ($basepath , $dummy , $relationshipName , $layoutAdditions)
{
require_once 'modules/ModuleBuilder/parsers/views/GridLayoutMetaDataParser.php' ;
// 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
// would be better if GridLayoutMetaDataParser could handle this gracefully, so we don't have to maintain this list here
$invalidModules = array ( 'emails' , 'kbdocuments' ) ;
$fieldsToAdd = array();
foreach ( $layoutAdditions as $deployedModuleName => $fieldName )
{
if ( ! in_array( strtolower ( $deployedModuleName ) , $invalidModules ) ) {
foreach ( array ( MB_EDITVIEW , MB_DETAILVIEW ) as $view )
{
$GLOBALS [ 'log' ]->debug ( get_class ( $this ) . ": adding $fieldName to $view layout for module $deployedModuleName" ) ;
$parsedName = self::parseDeployedModuleName ( $deployedModuleName ) ;
if (! isset ( $parsedName [ 'packageName' ] ))
{
$fieldsToAdd [$parsedName [ 'moduleName' ]] = $fieldName;
}
//Bug 22348: We should add in the field for custom modules not in this package, if they have been deployed.
else if ($parsedName [ 'packageName' ] != $this->packageName
&& isset ( $GLOBALS [ 'beanList' ] [ $deployedModuleName ])){
$fieldsToAdd [$deployedModuleName] = $fieldName;
}
}
}
}
return array(array('additional_fields' => $fieldsToAdd));
}
}

View File

@@ -0,0 +1,301 @@
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
require_once 'modules/ModuleBuilder/parsers/constants.php' ;
/*
* Abstract base clase for Parser Implementations (using a Bridge Pattern)
* The Implementations hide the differences between :
* - Deployed modules (such as OOB modules and deployed ModuleBuilder modules) that are located in the /modules directory and have metadata in modules/<name>/metadata and in the custom directory
* - WIP modules which are being worked on in ModuleBuilder and that are located in custom
*/
require_once 'modules/ModuleBuilder/parsers/views/History.php' ;
abstract class AbstractMetaDataImplementation
{
protected $_view ;
protected $_moduleName ;
protected $_viewdefs ;
protected $_originalViewdefs = array();
protected $_fielddefs ;
protected $_sourceFilename = '' ; // the name of the file from which we loaded the definition we're working on - needed when we come to write out the historical record
// would like this to be a constant, but alas, constants cannot contain arrays...
protected $_fileVariables = array (
MB_DASHLETSEARCH => 'dashletData',
MB_DASHLET => 'dashletData',
MB_POPUPSEARCH => 'popupMeta',
MB_POPUPLIST => 'popupMeta',
MB_LISTVIEW => 'listViewDefs',
MB_BASICSEARCH => 'searchdefs',
MB_ADVANCEDSEARCH => 'searchdefs',
MB_EDITVIEW => 'viewdefs',
MB_DETAILVIEW => 'viewdefs',
MB_QUICKCREATE => 'viewdefs',
) ;
/*
* Getters for the definitions loaded by the Constructor
*/
function getViewdefs ()
{
$GLOBALS['log']->debug( get_class ( $this ) . '->getViewdefs:'.print_r($this->_viewdefs,true) ) ;
return $this->_viewdefs ;
}
function getOriginalViewdefs() {
return $this->_originalViewdefs;
}
function getFielddefs ()
{
return $this->_fielddefs ;
}
/*
* Obtain a new accessor for the history of this layout
* Ideally the History object would be a singleton; however given the use case (modulebuilder/studio) it's unlikely to be an issue
*/
function getHistory ()
{
return $this->_history ;
}
/*
* Load a layout from a file, given a filename
* Doesn't do any preprocessing on the viewdefs - just returns them as found for other classes to make sense of
* @param string filename The full path to the file containing the layout
* @return array The layout, null if the file does not exist
*/
protected function _loadFromFile ($filename)
{
// BEGIN ASSERTIONS
if (! file_exists ( $filename ))
{
return null ;
}
// END ASSERTIONS
$GLOBALS['log']->debug(get_class($this)."->_loadFromFile(): reading from ".$filename );
require $filename ; // loads the viewdef - must be a require not require_once to ensure can reload if called twice in succession
// Check to see if we have the module name set as a variable rather than embedded in the $viewdef array
// If we do, then we have to preserve the module variable when we write the file back out
// This is a format used by ModuleBuilder templated modules to speed the renaming of modules
// OOB Sugar modules don't use this format
$moduleVariables = array ( 'module_name' , '_module_name' , 'OBJECT_NAME' , '_object_name' ) ;
$variables = array ( ) ;
foreach ( $moduleVariables as $name )
{
if (isset ( $$name ))
{
$variables [ $name ] = $$name ;
}
}
// Extract the layout definition from the loaded file - the layout definition is held under a variable name that varies between the various layout types (e.g., listviews hold it in listViewDefs, editviews in viewdefs)
$viewVariable = $this->_fileVariables [ $this->_view ] ;
$defs = $$viewVariable ;
// Now tidy up the module name in the viewdef array
// MB created definitions store the defs under packagename_modulename and later methods that expect to find them under modulename will fail
if (isset ( $variables [ 'module_name' ] ))
{
$mbName = $variables [ 'module_name' ] ;
if ($mbName != $this->_moduleName)
{
$defs [ $this->_moduleName ] = $defs [ $mbName ] ;
unset ( $defs [ $mbName ] ) ;
}
}
$this->_variables = $variables ;
// now remove the modulename preamble from the loaded defs
reset($defs);
$temp = each($defs);
$GLOBALS['log']->debug( get_class ( $this ) . "->_loadFromFile: returning ".print_r($temp['value'],true)) ;
return $temp['value']; // 'value' contains the value part of 'key'=>'value' part
}
protected function _loadFromPopupFile ($filename, $mod, $view, $forSave = false)
{
// BEGIN ASSERTIONS
if (!file_exists ( $filename ))
{
return null ;
}
// END ASSERTIONS
$GLOBALS['log']->debug(get_class($this)."->_loadFromFile(): reading from ".$filename );
if(!empty($mod)){
$oldModStrings = $GLOBALS['mod_strings'];
$GLOBALS['mod_strings'] = $mod;
}
require $filename ; // loads the viewdef - must be a require not require_once to ensure can reload if called twice in succession
$viewVariable = $this->_fileVariables [ $this->_view ] ;
$defs = $$viewVariable ;
if(!$forSave){
//Now we will unset the reserve field in pop definition file.
$limitFields = PopupMetaDataParser::$reserveProperties;
foreach($limitFields as $v){
if(isset($defs[$v])){
unset($defs[$v]);
}
}
if(isset($defs[PopupMetaDataParser::$defsMap[$view]])){
$defs = $defs[PopupMetaDataParser::$defsMap[$view]];
}else{
//If there are no defs for this view, grab them from the non-popup view
if ($view == MB_POPUPLIST)
{
$this->_view = MB_LISTVIEW;
$defs = $this->_loadFromFile ( $this->getFileName ( MB_LISTVIEW, $this->_moduleName, MB_CUSTOMMETADATALOCATION ) ) ;
if ($defs == null)
$defs = $this->_loadFromFile ( $this->getFileName ( MB_LISTVIEW, $this->_moduleName, MB_BASEMETADATALOCATION ) ) ;
$this->_view = $view;
}
else if ($view == MB_POPUPSEARCH)
{
$this->_view = MB_ADVANCEDSEARCH;
$defs = $this->_loadFromFile ( $this->getFileName ( MB_ADVANCEDSEARCH, $this->_moduleName, MB_CUSTOMMETADATALOCATION ) ) ;
if ($defs == null)
$defs = $this->_loadFromFile ( $this->getFileName ( MB_ADVANCEDSEARCH, $this->_moduleName, MB_BASEMETADATALOCATION ) ) ;
if (isset($defs['layout']) && isset($defs['layout']['advanced_search']))
$defs = $defs['layout']['advanced_search'];
$this->_view = $view;
}
if ($defs == null)
$defs = array();
}
}
$this->_variables = array();
if(!empty($oldModStrings)){
$GLOBALS['mod_strings'] = $oldModStrings;
}
return $defs;
}
/*
* Save a layout to a file
* Must be the exact inverse of _loadFromFile
* Obtains the additional variables, such as module_name, to include in beginning of the file (as required by ModuleBuilder) from the internal variable _variables, set in the Constructor
* @param string filename The full path to the file to contain the layout
* @param array defs Array containing the layout definition; the top level should be the definition itself; not the modulename or viewdef= preambles found in the file definitions
* @param boolean useVariables Write out with placeholder entries for module name and object name - used by ModuleBuilder modules
*/
protected function _saveToFile ($filename , $defs , $useVariables = true, $forPopup = false )
{
mkdir_recursive ( dirname ( $filename ) ) ;
$useVariables = (count ( $this->_variables ) > 0) && $useVariables ; // only makes sense to do the variable replace if we have variables to replace...
// create the new metadata file contents, and write it out
$out = "<?php\n" ;
if ($useVariables)
{
// write out the $<variable>=<modulename> lines
foreach ( $this->_variables as $key => $value )
{
$out .= "\$$key = '" . $value . "';\n" ;
}
}
$viewVariable = $this->_fileVariables [ $this->_view ] ;
if($forPopup){
$out .= "\$$viewVariable = \n" . var_export_helper ( $defs ) ;
}else{
$out .= "\$$viewVariable [".(($useVariables) ? '$module_name' : "'$this->_moduleName'")."] = \n" . var_export_helper ( $defs ) ;
}
$out .= ";\n?>\n" ;
if ( file_put_contents ( $filename, $out ) === false)
$GLOBALS [ 'log' ]->fatal ( get_class($this).": could not write new viewdef file " . $filename ) ;
}
/*
* Fielddefs are obtained from two locations:
*
* 1. The starting point is the module's fielddefs, sourced from the Bean
* 2. Second comes any overrides from the layouts themselves. Note though that only visible fields are included in a layoutdef, which
* means fields that aren't present in the current layout may have a layout defined in a lower-priority layoutdef, for example, the base layoutdef
*
* Thus to determine the current fielddef for any given field, we take the fielddef defined in the module's Bean and then override with first the base layout,
* then the customlayout, then finally the working layout...
*
* The complication is that although generating these merged fielddefs is naturally a method of the implementation, not the parser,
* we therefore lack knowledge as to which type of layout we are merging - EditView or ListView. So we can't use internal knowledge of the
* layout to locate the field definitions. Instead, we need to look for sections of the layout that match the template for a field definition...
*/
function _mergeFielddefs ( &$fielddefs , $layout )
{
foreach ( $layout as $key => $def )
{
if ( (string) $key == 'templateMeta' )
continue ;
if ( is_array ( $def ) )
{
if ( isset ( $def [ 'name' ] ) && ! is_array ( $def [ 'name' ] ) ) // found a 'name' definition, that is not the definition of a field called name :)
{
// if this is a module field, then merge in the definition, otherwise this is a new field defined in the layout, so just take the definition
$fielddefs [ $def [ 'name'] ] = ( isset ($fielddefs [ $def [ 'name' ] ] ) ) ? array_merge ( $fielddefs [ $def [ 'name' ] ], $def ) : $def ;
}
else if ( isset ( $def [ 'label' ] ) || isset ( $def [ 'vname' ] ) || isset($def ['widget_class']) ) // dealing with a listlayout which lacks 'name' keys, but which does have 'label' keys
{
$key = strtolower ( $key ) ;
$fielddefs [ $key ] = ( isset ($fielddefs [ $key ] ) ) ? array_merge ( $fielddefs [ $key ], $def ) : $def ;
}
else
$this->_mergeFielddefs( $fielddefs , $def ) ;
}
}
}
}
?>

View File

@@ -0,0 +1,127 @@
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
abstract class AbstractMetaDataParser
{
//Make these properties public for now until we can create some usefull accessors
public $_fielddefs ;
public $_viewdefs ;
protected $_moduleName ;
protected $implementation ; // the DeployedMetaDataImplementation or UndeployedMetaDataImplementation object to handle the reading and writing of files and field data
function getLayoutAsArray ()
{
$viewdefs = $this->_panels ;
}
function getLanguage ()
{
return $this->implementation->getLanguage () ;
}
function getHistory ()
{
return $this->implementation->getHistory () ;
}
function removeField ($fieldName)
{
return false;
}
/*
* Is this field something we wish to show in Studio/ModuleBuilder layout editors?
* @param array $def Field definition in the standard SugarBean field definition format - name, vname, type and so on
* @return boolean True if ok to show, false otherwise
*/
static function validField ( $def, $view = "")
{
//Studio invisible fields should always be hidden
if (isset ($def[ 'studio' ] ) )
{
if (is_array($def [ 'studio' ]))
{
if (!empty($view) && isset($def [ 'studio' ][$view]))
return $def [ 'studio' ][$view] !== false && $def [ 'studio' ][$view] != 'false' && $def [ 'studio' ][$view] != 'hidden';
if (isset($def [ 'studio' ]['visible']))
return $def [ 'studio' ]['visible'];
} else {
return ($def [ 'studio' ] != 'false' && $def [ 'studio' ] != 'hidden' && $def [ 'studio' ] !== false) ;
}
}
// bug 19656: this test changed after 5.0.0b - we now remove all ID type fields - whether set as type, or dbtype, from the fielddefs
return
(
(
(empty ( $def [ 'source' ] ) || $def [ 'source' ] == 'db' || $def [ 'source' ] == 'custom_fields')
&& isset($def [ 'type' ]) && $def [ 'type' ] != 'id' && $def [ 'type' ] != 'parent_type'
&& (empty ( $def [ 'dbType' ] ) || $def [ 'dbType' ] != 'id')
&& ( isset ( $def [ 'name' ] ) && strcmp ( $def [ 'name' ] , 'deleted' ) != 0 )
) // db and custom fields that aren't ID fields
||
// exclude fields named *_name regardless of their type...just convention
(isset ( $def [ 'name' ] ) && substr ( $def [ 'name' ], -5 ) === '_name' ) ) ;
}
protected function _standardizeFieldLabels ( &$fielddefs )
{
foreach ( $fielddefs as $key => $def )
{
if ( !isset ($def [ 'label' ] ) )
{
$fielddefs [ $key ] [ 'label'] = ( isset ( $def [ 'vname' ] ) ) ? $def [ 'vname' ] : $key ;
}
}
}
abstract static function _trimFieldDefs ( $def ) ;
public function getRequiredFields(){
$fieldDefs = $this->implementation->getFielddefs();
$newAry = array();
foreach($fieldDefs as $field){
if(isset($field['required']) && $field['required'] && isset($field['name'])){
array_push($newAry , '"'.$field['name'].'"');
}
}
return $newAry;
}
}
?>

View File

@@ -0,0 +1,175 @@
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
require_once ('modules/ModuleBuilder/parsers/views/ListLayoutMetaDataParser.php') ;
require_once ('modules/ModuleBuilder/parsers/views/SearchViewMetaDataParser.php') ;
require_once 'modules/ModuleBuilder/parsers/constants.php' ;
class DashletMetaDataParser extends ListLayoutMetaDataParser
{
// Columns is used by the view to construct the listview - each column is built by calling the named function
public $columns = array ( 'LBL_DEFAULT' => 'getDefaultFields' , 'LBL_AVAILABLE' => 'getAdditionalFields' , 'LBL_HIDDEN' => 'getAvailableFields' ) ;
/*
* Constructor
* Must set:
* $this->columns Array of 'Column LBL'=>function_to_retrieve_fields_for_this_column() - expected by the view
*
* @param string moduleName The name of the module to which this listview belongs
* @param string packageName If not empty, the name of the package to which this listview belongs
*/
function __construct ($view, $moduleName , $packageName = '')
{
$this->search = ($view == MB_DASHLETSEARCH) ? true : false;
$this->_moduleName = $moduleName;
$this->_packageName = $packageName;
$this->_view = $view ;
if ($this->search)
{
$this->columns = array ( 'LBL_DEFAULT' => 'getAdditionalFields' , 'LBL_HIDDEN' => 'getAvailableFields' ) ;
parent::__construct ( MB_DASHLETSEARCH, $moduleName, $packageName ) ;
} else
{
parent::__construct ( MB_DASHLET, $moduleName, $packageName ) ;
}
$this->_viewdefs = $this->mergeFieldDefinitions($this->_viewdefs, $this->_fielddefs);
}
/**
* Dashlets contain both a searchview and list view definition, therefore we need to merge only the relevant info
*/
function mergeFieldDefinitions ( $viewdefs, $fielddefs ) {
if ($this->_view == MB_DASHLETSEARCH && isset($viewdefs['searchfields']))
{
//Remove any relate fields from the possible defs as they will break the homepage
foreach($fielddefs as $id=>$def) {
if (isset($def['type']) && $def['type'] == 'relate') {
if( isset($fielddefs[$id]['id_name'])){
$fielddefs[$fielddefs[$id]['id_name']] = $def;
unset($fielddefs[$id]);
}
}
}
$viewdefs = array_change_key_case($viewdefs['searchfields']);
$viewdefs = $this->_viewdefs = $this->convertSearchToListDefs($viewdefs);
}
else if ($this->_view == MB_DASHLET && isset($viewdefs['columns']))
{
$viewdefs = $this->_viewdefs = array_change_key_case($viewdefs['columns']);
$viewdefs = $this->_viewdefs = $this->convertSearchToListDefs($viewdefs);
}
return $viewdefs;
}
function convertSearchToListDefs($defs) {
$temp = array();
foreach($defs as $key=>$value) {
$temp[$key] = $value;
if (!isset ($temp[$key]['name'])) {
$temp[$key]['name'] = $key;
}
}
return $temp;
}
private function ConvertSearchToDashletDefs($defs) {
$temp = array();
foreach($defs as $key=>$value) {
if($value['default']) {
//$temp[$key] = $value;
$temp[$key] = array('default' => '');
}else{
$temp[$key] = $value;
}
}
return $temp;
}
function handleSave ($populate = true)
{
if (empty ( $this->_packageName ))
{
foreach(array(MB_CUSTOMMETADATALOCATION , MB_BASEMETADATALOCATION) as $value){
$file = $this->implementation->getFileName(MB_DASHLET, $this->_moduleName, $value);
if(file_exists($file)){
break;
}
}
$writeTodashletName = $dashletName = $this->implementation->getLanguage().'Dashlet';
if(!file_exists($file)){
$file = "modules/{$this->_moduleName}/Dashlets/My{$this->_moduleName}Dashlet/My{$this->_moduleName}Dashlet.data.php";
$dashletName = 'My'.$this->implementation->getLanguage().'Dashlet';
}
$writeFile = $this->implementation->getFileName(MB_DASHLET, $this->_moduleName);
if(!file_exists($writeFile)){
mkdir_recursive ( dirname ( $writeFile ) ) ;
}
}
else{
$writeFile = $file = $this->implementation->getFileName(MB_DASHLET, $this->_moduleName, $this->_packageName);
$writeTodashletName = $dashletName =$this->implementation->module->key_name . 'Dashlet';
}
$this->implementation->_history->append ( $file ) ;
if ($populate)
$this->_populateFromRequest() ;
$out = "<?php\n" ;
require($file);
if (!isset($dashletData[$dashletName])) {
sugar_die ("unable to load Module Dashlet Definition");
}
if ($fh = sugar_fopen ( $writeFile, 'w' ))
{
if ($this->_view == MB_DASHLETSEARCH)
{
$dashletData[$dashletName]['searchFields'] = $this->ConvertSearchToDashletDefs($this->_viewdefs);
} else
{
$dashletData[$dashletName]['columns'] = $this->_viewdefs;
}
$out .= "\$dashletData['$writeTodashletName']['searchFields'] = " . var_export_helper ($dashletData[$dashletName]['searchFields']) . ";\n";
$out .= "\$dashletData['$writeTodashletName']['columns'] = " . var_export_helper ($dashletData[$dashletName]['columns']) . ";\n";
fputs ( $fh, $out) ;
fclose ( $fh ) ;
}
}
}
?>

View File

@@ -0,0 +1,364 @@
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
/*
* Implementation class (following a Bridge Pattern) for handling loading and saving deployed module metadata
* For example, listview or editview viewdefs
*/
require_once 'modules/ModuleBuilder/parsers/views/AbstractMetaDataImplementation.php' ;
require_once 'modules/ModuleBuilder/parsers/views/MetaDataImplementationInterface.php' ;
require_once 'modules/ModuleBuilder/parsers/views/ListLayoutMetaDataParser.php' ;
require_once 'modules/ModuleBuilder/parsers/views/GridLayoutMetaDataParser.php' ;
require_once 'modules/ModuleBuilder/parsers/views/PopupMetaDataParser.php' ;
require_once 'modules/ModuleBuilder/Module/StudioModuleFactory.php' ;
require_once 'modules/ModuleBuilder/parsers/constants.php' ;
class DeployedMetaDataImplementation extends AbstractMetaDataImplementation implements MetaDataImplementationInterface
{
/*
* Constructor
* @param string $view
* @param string $moduleName
* @throws Exception Thrown if the provided view doesn't exist for this module
*/
function __construct ($view , $moduleName)
{
// BEGIN ASSERTIONS
if (! isset ( $this->_fileVariables [ $view ] ))
{
sugar_die ( get_class ( $this ) . ": View $view is not supported" ) ;
}
if (! isset ( $GLOBALS [ 'beanList' ] [ $moduleName ] ))
{
sugar_die ( get_class ( $this ) . ": Modulename $moduleName is not a Deployed Module" ) ;
}
// END ASSERTIONS
$this->_view = strtolower ( $view ) ;
$this->_moduleName = $moduleName ;
$module = StudioModuleFactory::getStudioModule( $moduleName ) ;
$this->module_dir = $module->seed->module_dir;
$fielddefs = $module->getFields();
$loaded = null ;
foreach ( array ( MB_BASEMETADATALOCATION , MB_CUSTOMMETADATALOCATION , MB_WORKINGMETADATALOCATION , MB_HISTORYMETADATALOCATION ) as $type )
{
$this->_sourceFilename = $this->getFileName ( $view, $moduleName, $type ) ;
if($view == MB_POPUPSEARCH || $view == MB_POPUPLIST){
global $current_language;
$mod = return_module_language($current_language , $moduleName);
$layout = $this->_loadFromPopupFile ( $this->_sourceFilename , $mod, $view);
}else{
$layout = $this->_loadFromFile ( $this->_sourceFilename );
}
if ( null !== $layout )
{
// merge in the fielddefs from this layout
$this->_mergeFielddefs ( $fielddefs , $layout ) ;
$loaded = $layout ;
}
}
if ($loaded === null)
{
switch ( $view )
{
case MB_QUICKCREATE:
// Special handling for QuickCreates - if we don't have a QuickCreate definition in the usual places, then use an EditView
$loaded = $this->_loadFromFile ( $this->getFileName ( MB_EDITVIEW, $this->_moduleName, MB_BASEMETADATALOCATION ) ) ;
if ($loaded === null)
throw new Exception( get_class ( $this ) . ": cannot convert from EditView to QuickCreate for Module $this->_moduleName - definitions for EditView are missing" ) ;
// Now change the array index
$temp = $loaded [ GridLayoutMetaDataParser::$variableMap [ MB_EDITVIEW ] ] ;
unset ( $loaded [ GridLayoutMetaDataParser::$variableMap [ MB_EDITVIEW ] ] ) ;
$loaded [ GridLayoutMetaDataParser::$variableMap [ MB_QUICKCREATE ] ] = $temp ;
// finally, save out our new definition so that we have a base record for the history to work from
$this->_sourceFilename = self::getFileName ( MB_QUICKCREATE, $this->_moduleName, MB_CUSTOMMETADATALOCATION ) ;
$this->_saveToFile ( $this->_sourceFilename, $loaded ) ;
$this->_mergeFielddefs ( $fielddefs , $loaded ) ;
break;
case MB_DASHLETSEARCH:
case MB_DASHLET:
$type = $module->getType () ;
$this->_sourceFilename = self::getFileName ( $view, $moduleName, MB_CUSTOMMETADATALOCATION ) ;
$needSave = false;
if(file_exists( "custom/modules/{$moduleName}/metadata/".basename ( $this->_sourceFilename))){
$loaded = $this->_loadFromFile ( "custom/modules/{$moduleName}/metadata/".basename ( $this->_sourceFilename) ) ;
}
elseif(file_exists(
"modules/{$moduleName}/Dashlets/My{$moduleName}Dashlet/My{$moduleName}Dashlet.data.php")){
$loaded = $this->_loadFromFile ( "modules/{$moduleName}/Dashlets/My{$moduleName}Dashlet/My{$moduleName}Dashlet.data.php");
}
else{
$loaded = $this->_loadFromFile ( "include/SugarObjects/templates/$type/metadata/".basename ( $this->_sourceFilename ) ) ;
$needSave = true;
}
if ($loaded === null)
throw new Exception( get_class ( $this ) . ": cannot create dashlet view for module $moduleName - definitions for $view are missing in the SugarObject template for type $type" ) ;
$loaded = $this->replaceVariables($loaded, $module);
$temp = $this->_moduleName;
if($needSave){
$this->_moduleName = $this->_moduleName.'Dashlet';
$this->_saveToFile ( $this->_sourceFilename, $loaded,false) ; // write out without the placeholder module_name and object
$this->_moduleName = $temp;
unset($temp);
}
$this->_mergeFielddefs ( $fielddefs , $loaded ) ;
break;
case MB_POPUPLIST:
case MB_POPUPSEARCH:
$type = $module->getType () ;
$this->_sourceFilename = self::getFileName ( $view, $moduleName, MB_CUSTOMMETADATALOCATION ) ;
// Now we can copy the wireless view from the template
global $current_language;
$mod = return_module_language($current_language , $moduleName);
$loadedForWrite = $this->_loadFromPopupFile ( "include/SugarObjects/templates/$type/metadata/".basename ( $this->_sourceFilename ) , $mod, $view, true);
if ($loadedForWrite === null)
throw new Exception( get_class ( $this ) . ": cannot create popup view for module $moduleName - definitions for $view are missing in the SugarObject template for type $type" ) ;
$loadedForWrite = $this->replaceVariables($loadedForWrite, $module);
$this->_saveToFile ( $this->_sourceFilename, $loadedForWrite , false , true) ; // write out without the placeholder module_name and object
$loaded = $this->_loadFromPopupFile ( "include/SugarObjects/templates/$type/metadata/".basename ( $this->_sourceFilename ) , $mod, $view);
$this->_mergeFielddefs ( $fielddefs , $loaded ) ;
break;
default:
}
if ( $loaded === null )
throw new Exception( get_class ( $this ) . ": view definitions for View $this->_view and Module $this->_moduleName are missing" ) ;
}
$this->_viewdefs = $loaded ;
// Set the original Viewdefs - required to ensure we don't lose fields from the base layout
// Check the base location first, then if nothing is there (which for example, will be the case for some QuickCreates, and some mobile layouts - see above)
// we need to check the custom location where the derived layouts will be
foreach ( array ( MB_BASEMETADATALOCATION , MB_CUSTOMMETADATALOCATION ) as $type )
{
$sourceFilename = $this->getFileName ( $view, $moduleName, $type ) ;
if($view == MB_POPUPSEARCH || $view == MB_POPUPLIST){
global $current_language;
$mod = return_module_language($current_language , $moduleName);
$layout = $this->_loadFromPopupFile ( $sourceFilename , $mod, $view);
}else{
$layout = $this->_loadFromFile ( $sourceFilename );
}
if ( null !== ($layout ) )
{
$this->_originalViewdefs = $layout ;
break ;
}
}
//For quick create viewdefs, if there is no quickcreatedefs.php under MB_BASEMETADATALOCATION, the original defs is editview defs.
if ($view == MB_QUICKCREATE) {
foreach(array(MB_QUICKCREATE, MB_EDITVIEW) as $v){
$sourceFilename = $this->getFileName($v, $moduleName, MB_BASEMETADATALOCATION ) ;
if (file_exists($sourceFilename )) {
$layout = $this->_loadFromFile($sourceFilename );
if (null !== $layout && isset($layout[GridLayoutMetaDataParser::$variableMap[$v]])) {
$layout = array(GridLayoutMetaDataParser::$variableMap[MB_QUICKCREATE] => $layout[GridLayoutMetaDataParser::$variableMap[$v]]);
break;
}
}
}
if (null === $layout) {
$sourceFilename = $this->getFileName($view, $moduleName, MB_CUSTOMMETADATALOCATION );
$layout = $this->_loadFromFile($sourceFilename );
}
if (null !== $layout ) {
$this->_originalViewdefs = $layout ;
}
}
$this->_fielddefs = $fielddefs ;
$this->_history = new History ( $this->getFileName ( $view, $moduleName, MB_HISTORYMETADATALOCATION ) ) ;
}
function getLanguage ()
{
return $this->_moduleName ;
}
function getOriginalViewdefs()
{
return $this->_originalViewdefs;
}
/*
* Save a draft layout
* @param array defs Layout definition in the same format as received by the constructor
*/
function save ($defs)
{
//If we are pulling from the History Location, that means we did a restore, and we need to save the history for the previous file.
if ($this->_sourceFilename == $this->getFileName ( $this->_view, $this->_moduleName, MB_HISTORYMETADATALOCATION )) {
foreach ( array ( MB_WORKINGMETADATALOCATION , MB_CUSTOMMETADATALOCATION , MB_BASEMETADATALOCATION ) as $type ) {
if (file_exists($this->getFileName ( $this->_view, $this->_moduleName, $type ))) {
$this->_history->append ( $this->getFileName ( $this->_view, $this->_moduleName, $type )) ;
break;
}
}
} else {
$this->_history->append ( $this->_sourceFilename ) ;
}
$GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->save(): writing to " . $this->getFileName ( $this->_view, $this->_moduleName, MB_WORKINGMETADATALOCATION ) ) ;
$this->_saveToFile ( $this->getFileName ( $this->_view, $this->_moduleName, MB_WORKINGMETADATALOCATION ), $defs ) ;
}
/*
* Deploy a layout
* @param array defs Layout definition in the same format as received by the constructor
*/
function deploy ($defs)
{
if ($this->_sourceFilename == $this->getFileName ( $this->_view, $this->_moduleName, MB_HISTORYMETADATALOCATION )) {
foreach ( array ( MB_WORKINGMETADATALOCATION , MB_CUSTOMMETADATALOCATION , MB_BASEMETADATALOCATION ) as $type ) {
if (file_exists($this->getFileName ( $this->_view, $this->_moduleName, $type ))) {
$this->_history->append ( $this->getFileName ( $this->_view, $this->_moduleName, $type )) ;
break;
}
}
} else {
$this->_history->append ( $this->_sourceFilename ) ;
}
// when we deploy get rid of the working file; we have the changes in the MB_CUSTOMMETADATALOCATION so no need for a redundant copy in MB_WORKINGMETADATALOCATION
// this also simplifies manual editing of layouts. You can now switch back and forth between Studio and manual changes without having to keep these two locations in sync
$workingFilename = $this->getFileName ( $this->_view, $this->_moduleName, MB_WORKINGMETADATALOCATION ) ;
if (file_exists ( $workingFilename ))
unlink ( $this->getFileName ( $this->_view, $this->_moduleName, MB_WORKINGMETADATALOCATION ) ) ;
$filename = $this->getFileName ( $this->_view, $this->_moduleName, MB_CUSTOMMETADATALOCATION ) ;
$GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->deploy(): writing to " . $filename ) ;
$this->_saveToFile ( $filename, $defs ) ;
// now clear the cache so that the results are immediately visible
include_once ('include/TemplateHandler/TemplateHandler.php') ;
TemplateHandler::clearCache ( $this->_moduleName ) ;
}
/*
* Construct a full pathname for the requested metadata
* Can be called statically
* @param string view The view type, that is, EditView, DetailView etc
* @param string modulename The name of the module that will use this layout
* @param string type
*/
public static function getFileName ($view , $moduleName , $type = MB_CUSTOMMETADATALOCATION)
{
$pathMap = array ( MB_BASEMETADATALOCATION => '' , MB_CUSTOMMETADATALOCATION => 'custom/' , MB_WORKINGMETADATALOCATION => 'custom/working/' , MB_HISTORYMETADATALOCATION => 'custom/history/' ) ;
$type = strtolower ( $type ) ;
$filenames = array (
MB_DASHLETSEARCH => 'dashletviewdefs',
MB_DASHLET => 'dashletviewdefs',
MB_POPUPSEARCH => 'popupdefs',
MB_POPUPLIST => 'popupdefs',
MB_LISTVIEW => 'listviewdefs' ,
MB_BASICSEARCH => 'searchdefs' ,
MB_ADVANCEDSEARCH => 'searchdefs' ,
MB_EDITVIEW => 'editviewdefs' ,
MB_DETAILVIEW => 'detailviewdefs' ,
MB_QUICKCREATE => 'quickcreatedefs',
) ;
// BEGIN ASSERTIONS
if (! isset ( $pathMap [ $type ] ))
{
sugar_die ( "DeployedMetaDataImplementation->getFileName(): Type $type is not recognized" ) ;
}
if (! isset ( $filenames [ $view ] ))
{
sugar_die ( "DeployedMetaDataImplementation->getFileName(): View $view is not recognized" ) ;
}
// END ASSERTIONS
// Construct filename
return $pathMap [ $type ] . 'modules/' . $moduleName . '/metadata/' . $filenames [ $view ] . '.php' ;
}
private function replaceVariables($defs, $module) {
$var_values = array(
"<object_name>" => $module->seed->object_name,
"<_object_name>" => strtolower($module->seed->object_name),
"<OBJECT_NAME>" => strtoupper($module->seed->object_name),
"<module_name>" => $module->seed->module_dir,
'<_module_name>'=> strtolower ( $module->seed->module_dir )
);
return $this->recursiveVariableReplace($defs, $module, $var_values);
}
public function getModuleDir(){
return $this->module_dir;
}
private function recursiveVariableReplace($arr, $module, $replacements) {
$ret = array();
foreach ($arr as $key => $val) {
if (is_array($val)) {
$newkey = $key;
$val = $this->recursiveVariableReplace($val, $module, $replacements);
foreach ($replacements as $var => $rep) {
$newkey = str_replace($var, $rep, $newkey);
}
$ret[$newkey] = $val;
} else {
$newkey = $key;
$newval = $val;
foreach ($replacements as $var => $rep) {
$newkey = str_replace($var, $rep, $newkey);
$newval = str_replace($var, $rep, $newval);
}
$ret[$newkey] = $newval;
}
}
return $ret;
}
}

View File

@@ -0,0 +1,168 @@
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
/*
* Changes to AbstractSubpanelImplementation for DeployedSubpanels
* The main differences are in the load and save of the definitions
* For subpanels we must make use of the SubPanelDefinitions class to do this; this also means that the history mechanism,
* which tracks files, not objects, needs us to create an intermediate file representation of the definition that it can manage and restore
*/
require_once 'modules/ModuleBuilder/parsers/views/MetaDataImplementationInterface.php' ;
require_once 'modules/ModuleBuilder/parsers/views/AbstractMetaDataImplementation.php' ;
require_once 'modules/ModuleBuilder/parsers/constants.php' ;
class DeployedSubpanelImplementation extends AbstractMetaDataImplementation implements MetaDataImplementationInterface
{
const HISTORYFILENAME = 'restored.php' ;
const HISTORYVARIABLENAME = 'layout_defs' ;
private $_subpanelName ;
private $_aSubPanelObject ; // an aSubPanel Object representing the current subpanel
/*
* Constructor
* @param string subpanelName The name of this subpanel
* @param string moduleName The name of the module to which this subpanel belongs
*/
function __construct ($subpanelName , $moduleName)
{
$GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->__construct($subpanelName , $moduleName)" ) ;
$this->_subpanelName = $subpanelName ;
$this->_moduleName = $moduleName ;
// BEGIN ASSERTIONS
if (! isset ( $GLOBALS [ 'beanList' ] [ $moduleName ] ))
{
sugar_die ( get_class ( $this ) . ": Modulename $moduleName is not a Deployed Module" ) ;
}
// END ASSERTIONS
$this->historyPathname = 'custom/history/modules/' . $moduleName . '/subpanels/' . $subpanelName . '/' . self::HISTORYFILENAME ;
$this->_history = new History ( $this->historyPathname ) ;
$module = get_module_info ( $moduleName ) ;
require_once ('include/SubPanel/SubPanelDefinitions.php') ;
// retrieve the definitions for all the available subpanels for this module from the subpanel
$spd = new SubPanelDefinitions ( $module ) ;
// Get the lists of fields already in the subpanel and those that can be added in
// Get the fields lists from an aSubPanel object describing this subpanel from the SubPanelDefinitions object
$this->_viewdefs = array ( ) ;
$this->_fielddefs = array ( ) ;
$this->_language = '' ;
if (! empty ( $spd->layout_defs ))
if (array_key_exists ( strtolower ( $subpanelName ), $spd->layout_defs [ 'subpanel_setup' ] ))
{
//First load the original defs from the module folder
$originalSubpanel = $spd->load_subpanel( $subpanelName , false, true);
$this->_fullFielddefs = $originalSubpanel->get_list_fields ();
$this->_mergeFielddefs ( $this->_fielddefs , $this->_fullFielddefs ) ;
$this->_aSubPanelObject = $spd->load_subpanel ( $subpanelName ) ;
// now check if there is a restored subpanel in the history area - if there is, then go ahead and use it
if (file_exists ( $this->historyPathname ))
{
// load in the subpanelDefOverride from the history file
$GLOBALS [ 'log' ]->debug ( get_class ( $this ) . ": loading from history" ) ;
require $this->historyPathname ;
$this->_viewdefs = $layout_defs;
} else
{
$this->_viewdefs = $this->_aSubPanelObject->get_list_fields () ;
}
// don't attempt to access the template_instance property if our subpanel represents a collection, as it won't be there - the sub-sub-panels get this value instead
if ( ! $this->_aSubPanelObject->isCollection() )
$this->_language = $this->_aSubPanelObject->template_instance->module_dir ;
// Retrieve a copy of the bean for the parent module of this subpanel - so we can find additional fields for the layout
$subPanelParentModuleName = $this->_aSubPanelObject->get_module_name () ;
$beanListLower = array_change_key_case ( $GLOBALS [ 'beanList' ] ) ;
if (! empty ( $subPanelParentModuleName ) && isset ( $beanListLower [ strtolower ( $subPanelParentModuleName ) ] ))
{
$subPanelParentModule = get_module_info ( $subPanelParentModuleName ) ;
// Run through the preliminary list, keeping only those fields that are valid to include in a layout
foreach ( $subPanelParentModule->field_defs as $key => $def )
{
$key = strtolower ( $key ) ;
if (AbstractMetaDataParser::validField( $def ))
{
if ( ! isset ( $def [ 'label' ] ) )
$def [ 'label' ] = $def [ 'name' ] ;
$this->_fielddefs [ $key ] = $def ;
}
}
}
$this->_mergeFielddefs ( $this->_fielddefs , $this->_viewdefs ) ;
}
}
function getLanguage ()
{
return $this->_language ;
}
/*
* Save a definition that will be used to display a subpanel for $this->_moduleName
* @param array defs Layout definition in the same format as received by the constructor
*/
function deploy ($defs)
{
// first sort out the historical record...
write_array_to_file ( self::HISTORYVARIABLENAME, $this->_viewdefs, $this->historyPathname, 'w', '' ) ;
$this->_history->append ( $this->historyPathname ) ;
$this->_viewdefs = $defs ;
require_once 'include/SubPanel/SubPanel.php' ;
$subpanel = new SubPanel ( $this->_moduleName, 'fab4', $this->_subpanelName , $this->_aSubPanelObject ) ;
$subpanel->saveSubPanelDefOverride ( $this->_aSubPanelObject, 'list_fields', $defs ) ;
// now clear the cache so that the results are immediately visible
include_once ('include/TemplateHandler/TemplateHandler.php') ;
TemplateHandler::clearCache ( $this->_moduleName ) ;
}
}

View File

@@ -0,0 +1,723 @@
<?php
if (! defined ( 'sugarEntry' ) || ! sugarEntry)
die ( 'Not A Valid Entry Point' ) ;
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
require_once 'modules/ModuleBuilder/parsers/views/AbstractMetaDataParser.php' ;
require_once 'modules/ModuleBuilder/parsers/views/MetaDataParserInterface.php' ;
require_once 'modules/ModuleBuilder/parsers/constants.php' ;
class GridLayoutMetaDataParser extends AbstractMetaDataParser implements MetaDataParserInterface
{
static $variableMap = array (
MB_EDITVIEW => 'EditView' ,
MB_DETAILVIEW => 'DetailView' ,
MB_QUICKCREATE => 'QuickCreate',
) ;
protected $FILLER ;
/*
* Constructor
* @param string view The view type, that is, editview, searchview etc
* @param string moduleName The name of the module to which this view belongs
* @param string packageName If not empty, the name of the package to which this view belongs
*/
function __construct ($view , $moduleName , $packageName = '')
{
$GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->__construct( {$view} , {$moduleName} , {$packageName} )" ) ;
$view = strtolower ( $view ) ;
// BEGIN ASSERTIONS
if (! isset ( self::$variableMap [ $view ] ) )
sugar_die ( get_class ( $this ) . ": View $view is not supported" ) ;
// END ASSERTIONS
$this->FILLER = array ( 'name' => MBConstants::$FILLER['name'] , 'label' => translate ( MBConstants::$FILLER['label'] ) ) ;
$this->_moduleName = $moduleName ;
$this->_view = $view ;
if (empty ( $packageName ))
{
require_once 'modules/ModuleBuilder/parsers/views/DeployedMetaDataImplementation.php' ;
$this->implementation = new DeployedMetaDataImplementation ( $view, $moduleName, self::$variableMap ) ;
} else
{
require_once 'modules/ModuleBuilder/parsers/views/UndeployedMetaDataImplementation.php' ;
$this->implementation = new UndeployedMetaDataImplementation ( $view, $moduleName, $packageName ) ;
}
$viewdefs = $this->implementation->getViewdefs () ;
if (! isset ( $viewdefs [ self::$variableMap [ $view ] ] ))
sugar_die ( get_class ( $this ) . ": missing variable " . self::$variableMap [ $view ] . " in layout definition" ) ;
$viewdefs = $viewdefs [ self::$variableMap [ $view ] ] ;
if (! isset ( $viewdefs [ 'templateMeta' ] ))
sugar_die ( get_class ( $this ) . ": missing templateMeta section in layout definition (case sensitive)" ) ;
if (! isset ( $viewdefs [ 'panels' ] ))
sugar_die ( get_class ( $this ) . ": missing panels section in layout definition (case sensitive)" ) ;
$this->_viewdefs = $viewdefs ;
if ($this->getMaxColumns () < 1)
sugar_die ( get_class ( $this ) . ": maxColumns=" . $this->getMaxColumns () . " - must be greater than 0!" ) ;
$this->_fielddefs = $this->implementation->getFielddefs() ;
$this->_standardizeFieldLabels( $this->_fielddefs );
$this->_viewdefs [ 'panels' ] = $this->_convertFromCanonicalForm ( $this->_viewdefs [ 'panels' ] , $this->_fielddefs ) ; // put into our internal format
$this->_originalViewDef = $this->getFieldsFromLayout($this->implementation->getOriginalViewdefs ());
}
/*
* Save a draft layout
*/
function writeWorkingFile ()
{
$this->_populateFromRequest ( $this->_fielddefs ) ;
$viewdefs = $this->_viewdefs ;
$viewdefs [ 'panels' ] = $this->_convertToCanonicalForm ( $this->_viewdefs [ 'panels' ] , $this->_fielddefs ) ;
$this->implementation->save ( array ( self::$variableMap [ $this->_view ] => $viewdefs ) ) ;
}
/*
* Deploy the layout
* @param boolean $populate If true (default), then update the layout first with new layout information from the $_REQUEST array
*/
function handleSave ($populate = true)
{
$GLOBALS [ 'log' ]->info ( get_class ( $this ) . "->handleSave()" ) ;
if ($populate)
$this->_populateFromRequest ( $this->_fielddefs ) ;
$viewdefs = $this->_viewdefs ;
$viewdefs [ 'panels' ] = $this->_convertToCanonicalForm ( $this->_viewdefs [ 'panels' ] , $this->_fielddefs ) ;
$this->implementation->deploy ( array ( self::$variableMap [ $this->_view ] => $viewdefs ) ) ;
}
/*
* Return the layout, padded out with (empty) and (filler) fields ready for display
*/
function getLayout ()
{
$viewdefs = array () ;
$fielddefs = $this->_fielddefs;
$fielddefs [ $this->FILLER [ 'name' ] ] = $this->FILLER ;
$fielddefs [ MBConstants::$EMPTY [ 'name' ] ] = MBConstants::$EMPTY ;
foreach ( $this->_viewdefs [ 'panels' ] as $panelID => $panel )
{
foreach ( $panel as $rowID => $row )
{
foreach ( $row as $colID => $fieldname )
{
if (isset ($this->_fielddefs [ $fieldname ]))
{
$viewdefs [ $panelID ] [ $rowID ] [ $colID ] = self::_trimFieldDefs( $this->_fielddefs [ $fieldname ] ) ;
}
else if (isset($this->_originalViewDef [ $fieldname ]) && is_array($this->_originalViewDef [ $fieldname ]))
{
$viewdefs [ $panelID ] [ $rowID ] [ $colID ] = self::_trimFieldDefs( $this->_originalViewDef [ $fieldname ] ) ;
}
else
{
$viewdefs [ $panelID ] [ $rowID ] [ $colID ] = array("name" => $fieldname, "label" => $fieldname);
}
}
}
}
return $viewdefs ;
}
function getMaxColumns ()
{
if (!empty( $this->_viewdefs) && isset($this->_viewdefs [ 'templateMeta' ] [ 'maxColumns' ]))
{
return $this->_viewdefs [ 'templateMeta' ] [ 'maxColumns' ] ;
}else
{
return 2;
}
}
function getAvailableFields ()
{
// Obtain the full list of valid fields in this module
$availableFields = array () ;
foreach ( $this->_fielddefs as $key => $def )
{
if ( AbstractMetaDataParser::validField ( $def, $this->_view ) || isset($this->_originalViewDef[$key]) )
{
//If the field original label existing, we should use the original label instead the label in its fielddefs.
if(isset($this->_originalViewDef[$key]) && is_array($this->_originalViewDef[$key]) && isset($this->_originalViewDef[$key]['label'])){
$availableFields [ $key ] = array ( 'name' => $key , 'label' => $this->_originalViewDef[$key]['label']) ;
}else{
$availableFields [ $key ] = array ( 'name' => $key , 'label' => isset($def [ 'label' ]) ? $def [ 'label' ] : $def['vname'] ) ; // layouts use 'label' not 'vname' for the label entry
}
}
}
// Available fields are those that are in the Model and the original layout definition, but not already shown in the View
// So, because the formats of the two are different we brute force loop through View and unset the fields we find in a copy of Model
if (! empty ( $this->_viewdefs ))
{
foreach ( $this->_viewdefs [ 'panels' ] as $panel )
{
foreach ( $panel as $row )
{
foreach ( $row as $field )
{
unset ( $availableFields [ $field ] ) ;
}
}
}
}
return $availableFields ;
}
function getPanelDependency ( $panelID )
{
if ( ! isset ( $this->_viewdefs [ 'templateMeta' ][ 'dependency' ] ) && ! isset ( $this->_viewdefs [ 'templateMeta' ][ 'dependency' ] [ $panelID ] ) )
return false;
return $this->_viewdefs [ 'templateMeta' ][ 'dependency' ] [ $panelID ] ;
}
/*
* Add a new field to the layout
* If $panelID is passed in, attempt to add to that panel, otherwise add to the first panel
* The field is added in place of the first empty (not filler) slot after the last field in the panel; if that row is full, then a new row will be added to the end of the panel
* and the field added to the start of it.
* @param array $def Set of properties for the field, in same format as in the viewdefs
* @param string $panelID Identifier of the panel to add the field to; empty or false if we should use the first panel
*/
function addField ( $def , $panelID = FALSE)
{
if (count ( $this->_viewdefs [ 'panels' ] ) == 0)
{
$GLOBALS [ 'log' ]->error ( get_class ( $this ) . "->addField(): _viewdefs empty for module {$this->_moduleName} and view {$this->_view}" ) ;
}
// if a panelID was not provided, use the first available panel in the list
if (! $panelID)
{
$panels = array_keys ( $this->_viewdefs [ 'panels' ] ) ;
list ( $dummy, $panelID ) = each ( $panels ) ;
}
if (isset ( $this->_viewdefs [ 'panels' ] [ $panelID ] ))
{
$panel = $this->_viewdefs [ 'panels' ] [ $panelID ] ;
$lastrow = count ( $panel ) - 1 ; // index starts at 0
$maxColumns = $this->getMaxColumns () ;
for ( $column = 0 ; $column < $maxColumns ; $column ++ )
{
if (! isset ( $this->_viewdefs [ 'panels' ] [ $panelID ] [ $lastrow ] [ $column ] ) || ($this->_viewdefs [ 'panels' ] [ $panelID ] [ $lastrow ] [ $column ] [ 'name' ] == '(empty)'))
break ;
}
// if we're on the last column of the last row, start a new row
if ($column >= $maxColumns)
{
$lastrow ++ ;
$this->_viewdefs [ 'panels' ] [ $panelID ] [ $lastrow ] = array ( ) ;
$column = 0 ;
}
$this->_viewdefs [ 'panels' ] [ $panelID ] [ $lastrow ] [ $column ] = $def [ 'name' ] ;
// now update the fielddefs
if (isset($this->_fielddefs [ $def [ 'name' ] ]))
{
$this->_fielddefs [ $def [ 'name' ] ] = array_merge ( $this->_fielddefs [ $def [ 'name' ] ] , $def ) ;
} else
{
$this->_fielddefs [ $def [ 'name' ] ] = $def;
}
}
return true ;
}
/*
* Remove all instances of a field from the layout, and replace by (filler)
* Filler because we attempt to preserve the customized layout as much as possible - replacing by (empty) would mean that the positions or sizes of adjacent fields may change
* If the last row of a panel only consists of (filler) after removing the fields, then remove the row also. This undoes the standard addField() scenario;
* If the fields had been moved around in the layout however then this will not completely undo any addField()
* @param string $fieldName Name of the field to remove
* @return boolean True if the field was removed; false otherwise
*/
function removeField ($fieldName)
{
$GLOBALS [ 'log' ]->info ( get_class ( $this ) . "->removeField($fieldName)" ) ;
$result = false ;
reset ( $this->_viewdefs ) ;
$firstPanel = each ( $this->_viewdefs [ 'panels' ] ) ;
$firstPanelID = $firstPanel [ 'key' ] ;
foreach ( $this->_viewdefs [ 'panels' ] as $panelID => $panel )
{
$lastRowTouched = false ;
$lastRowID = count ( $this->_viewdefs [ 'panels' ] [ $panelID ] ) - 1 ; // zero offset
foreach ( $panel as $rowID => $row )
{
foreach ( $row as $colID => $field )
if ($field == $fieldName)
{
$lastRowTouched = $rowID ;
$this->_viewdefs [ 'panels' ] [ $panelID ] [ $rowID ] [ $colID ] = $this->FILLER [ 'name' ];
}
}
// if we removed a field from the last row of this panel, tidy up if the last row now consists only of (empty) or (filler)
if ( $lastRowTouched == $lastRowID )
{
$lastRow = $this->_viewdefs [ 'panels' ] [ $panelID ] [ $lastRowID ] ; // can't use 'end' for this as we need the key as well as the value...
$empty = true ;
foreach ( $lastRow as $colID => $field )
$empty &= $field == MBConstants::$EMPTY ['name' ] || $field == $this->FILLER [ 'name' ] ;
if ($empty)
{
unset ( $this->_viewdefs [ 'panels' ] [ $panelID ] [ $lastRowID ] ) ;
// if the row was the only one in the panel, and the panel is not the first (default) panel, then remove the panel also
if ( count ( $this->_viewdefs [ 'panels' ] [ $panelID ] ) == 0 && $panelID != $firstPanelID )
unset ( $this->_viewdefs [ 'panels' ] [ $panelID ] ) ;
}
}
$result |= ($lastRowTouched !== false ); // explicitly compare to false as row 0 will otherwise evaluate as false
}
return $result ;
}
function setPanelDependency ( $panelID , $dependency )
{
// only accept dependencies for pre-existing panels
if ( ! isset ( $this->_viewdefs [ 'panels' ] [ $panelID ] ) )
return false;
$this->_viewdefs [ 'templateMeta' ] [ 'dependency' ] [ $panelID ] = $dependency ;
return true ;
}
/*
* Return an integer value for the next unused panel identifier, such that it and any larger numbers are guaranteed to be unused already in the layout
* Necessary when adding new panels to a layout
* @return integer First unique panel ID suffix
*/
function getFirstNewPanelId ()
{
$firstNewPanelId = 0 ;
foreach ( $this->_viewdefs [ 'panels' ] as $panelID => $panel )
{
// strip out all but the numerics from the panelID - can't just use a cast as numbers may not be first in the string
for ( $i = 0, $result = '' ; $i < strlen ( $panelID ) ; $i ++ )
{
if (is_numeric ( $panelID [ $i ] ))
{
$result .= $panelID [ $i ] ;
}
}
$firstNewPanelId = max ( ( int ) $result, $firstNewPanelId ) ;
}
return $firstNewPanelId + 1 ;
}
/*
* Load the panel layout from the submitted form and update the _viewdefs
*/
protected function _populateFromRequest ( &$fielddefs )
{
$GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->populateFromRequest()" ) ;
$i = 1 ;
// set up the map of panel# (as provided in the _REQUEST) to panel ID (as used in $this->_viewdefs['panels'])
$i = 1 ;
foreach ( $this->_viewdefs [ 'panels' ] as $panelID => $panel )
{
$panelMap [ $i ++ ] = $panelID ;
}
foreach ( $_REQUEST as $key => $displayLabel )
{
$components = explode ( '-', $key ) ;
if ($components [ 0 ] == 'panel' && $components [ 2 ] == 'label')
{
$panelMap [ $components [ '1' ] ] = $displayLabel ;
}
}
$this->_viewdefs [ 'panels' ] = array () ; // because the new field properties should replace the old fields, not be merged
// run through the $_REQUEST twice - first to obtain the fieldnames, the second to update the field properties
for ( $pass=1 ; $pass<=2 ; $pass++ )
{
foreach ( $_REQUEST as $slot => $value )
{
$slotComponents = explode ( '-', $slot ) ; // [0] = 'slot', [1] = panel #, [2] = slot #, [3] = property name
if ($slotComponents [ 0 ] == 'slot')
{
$slotNumber = $slotComponents [ '2' ] ;
$panelID = $panelMap [ $slotComponents [ '1' ] ] ;
$rowID = floor ( $slotNumber / $this->getMaxColumns () ) ;
$colID = $slotNumber - ($rowID * $this->getMaxColumns ()) ;
$property = $slotComponents [ '3' ] ;
//If this field has a custom definition, copy that over
if ( $pass == 1 )
{
if ( $property == 'name' )
$this->_viewdefs [ 'panels' ] [ $panelID ] [ $rowID ] [ $colID ] = $value ;
} else
{
// update fielddefs for this property in the provided position
if ( isset ( $this->_viewdefs [ 'panels' ] [ $panelID ] [ $rowID ] [ $colID ] ) )
{
$fieldname = $this->_viewdefs [ 'panels' ] [ $panelID ] [ $rowID ] [ $colID ] ;
$fielddefs [ $fieldname ] [ $property ] = $value ;
}
}
}
}
}
//Set the tabs setting
if (isset($_REQUEST['panels_as_tabs']))
{
if ($_REQUEST['panels_as_tabs'] == false || $_REQUEST['panels_as_tabs'] == "false")
$this->setUseTabs( false );
else
$this->setUseTabs( true );
}
$GLOBALS [ 'log' ]->debug ( print_r ( $this->_viewdefs [ 'panels' ], true ) ) ;
}
/* Convert our internal format back to the standard Canonical MetaData layout
* First non-(empty) field goes in at column 0; all other (empty)'s removed
* Studio required fields are also added to the layout.
* Do this AFTER reading in all the $_REQUEST parameters as can't guarantee the order of those, and we need to operate on complete rows
*/
protected function _convertToCanonicalForm ( $panels , $fielddefs )
{
$previousViewDef = $this->getFieldsFromLayout($this->implementation->getViewdefs ());
$oldDefs = $this->implementation->getViewdefs ();
$currentFields = $this->getFieldsFromLayout($this->_viewdefs);
foreach($fielddefs as $field => $def)
{
if (self::fieldIsRequired($def) && !isset($currentFields[$field]))
{
//Use the previous viewdef if this field was on it.
if (isset($previousViewDef[$field]))
{
$def = $previousViewDef[$field];
}
//next see if the field was on the original layout.
else if (isset ($this->_originalViewDef [ $field ]))
{
$def = $this->_originalViewDef [ $field ] ;
}
//Otherwise make up a viewdef for it from field_defs
else
{
$def = self::_trimFieldDefs( $def ) ;
}
$this->addField($def);
}
}
foreach ( $panels as $panelID => $panel )
{
// remove all (empty)s
foreach ( $panel as $rowID => $row )
{
$startOfRow = true ;
$offset = 0 ;
foreach ( $row as $colID => $fieldname )
{
if ($fieldname == MBConstants::$EMPTY[ 'name' ])
{
// if a leading (empty) then remove (by noting that remaining fields need to be shuffled along)
if ($startOfRow)
{
$offset ++ ;
}
unset ( $row [ $colID ] ) ;
} else
{
$startOfRow = false ;
}
}
// reindex to remove leading (empty)s and replace fieldnames by full definition from fielddefs
$newRow = array ( ) ;
foreach ( $row as $colID => $fieldname )
{
if ($fieldname == null)
continue;
//Backwards compatibility and a safeguard against multiple calls to _convertToCanonicalForm
if(is_array($fieldname))
{
$newRow [ $colID - $offset ] = $fieldname;
continue;
}
//Replace (filler) with the empty string
if ($fieldname == $this->FILLER[ 'name' ]) {
$newRow [ $colID - $offset ] = '' ;
}
//Use the previous viewdef if this field was on it.
else if (isset($previousViewDef[$fieldname]))
{
$newRow [ $colID - $offset ] = $previousViewDef[$fieldname];
//We should copy over the tabindex if it is set.
if (isset ($fielddefs [ $fieldname ]) && !empty($fielddefs [ $fieldname ]['tabindex']))
$newRow [ $colID - $offset ]['tabindex'] = $fielddefs [ $fieldname ]['tabindex'];
}
//next see if the field was on the original layout.
else if (isset ($this->_originalViewDef [ $fieldname ]))
{
$newRow [ $colID - $offset ] = $this->_originalViewDef [ $fieldname ] ;
//We should copy over the tabindex if it is set.
if (isset ($fielddefs [ $fieldname ]) && !empty($fielddefs [ $fieldname ]['tabindex']))
$newRow [ $colID - $offset ]['tabindex'] = $fielddefs [ $fieldname ]['tabindex'];
}
//Otherwise make up a viewdef for it from field_defs
else if (isset ($fielddefs [ $fieldname ]))
{
$newRow [ $colID - $offset ] = self::_trimFieldDefs( $fielddefs [ $fieldname ] ) ;
}
//No additional info on this field can be found, jsut use the name;
else
{
$newRow [ $colID - $offset ] = $fieldname;
}
}
$panels [ $panelID ] [ $rowID ] = $newRow ;
}
}
return $panels ;
}
/*
* Convert from the standard MetaData format to our internal format
* Replace NULL with (filler) and missing entries with (empty)
*/
protected function _convertFromCanonicalForm ( $panels , $fielddefs )
{
if (empty ( $panels ))
return ;
// Fix for a flexibility in the format of the panel sections - if only one panel, then we don't have a panel level defined,
// it goes straight into rows
// See EditView2 for similar treatment
if (! empty ( $panels ) && count ( $panels ) > 0)
{
$keys = array_keys ( $panels ) ;
if (is_numeric ( $keys [ 0 ] ))
{
$defaultPanel = $panels ;
unset ( $panels ) ; //blow away current value
$panels [ 'default' ] = $defaultPanel ;
}
}
$newPanels = array ( ) ;
// replace '' with (filler)
foreach ( $panels as $panelID => $panel )
{
foreach ( $panel as $rowID => $row )
{
$cols = 0;
foreach ( $row as $colID => $col )
{
if ( ! empty ( $col ) )
{
if ( is_string ( $col ))
{
$fieldname = $col ;
} else if (! empty ( $col [ 'name' ] ))
{
$fieldname = $col [ 'name' ] ;
}
} else
{
$fieldname = $this->FILLER['name'] ;
}
$newPanels [ $panelID ] [ $rowID ] [ $cols ] = $fieldname ;
$cols++;
}
}
}
// replace missing fields with (empty)
foreach ( $newPanels as $panelID => $panel )
{
$column = 0 ;
foreach ( $panel as $rowID => $row )
{
// pad between fields on a row
foreach ( $row as $colID => $col )
{
for ( $i = $column + 1 ; $i < $colID ; $i ++ )
{
$row [ $i ] = MBConstants::$EMPTY ['name'];
}
$column = $colID ;
}
// now pad out to the end of the row
if (($column + 1) < $this->getMaxColumns ())
{ // last column is maxColumns-1
for ( $i = $column + 1 ; $i < $this->getMaxColumns () ; $i ++ )
{
$row [ $i ] = MBConstants::$EMPTY ['name'] ;
}
}
ksort ( $row ) ;
$newPanels [ $panelID ] [ $rowID ] = $row ;
}
}
return $newPanels ;
}
protected function getFieldsFromLayout($viewdef) {
if (isset($viewdef['panels']))
{
$panels = $viewdef['panels'];
} else {
$panels = $viewdef[self::$variableMap [ $this->_view ] ]['panels'];
}
$ret = array();
if (is_array($panels))
{
foreach ( $panels as $rows) {
foreach ($rows as $fields) {
//wireless layouts have one less level of depth
if (is_array($fields) && isset($fields['name'])) {
$ret[$fields['name']] = $fields;
continue;
}
if (!is_array($fields)) {
$ret[$fields] = $fields;
continue;
}
foreach ($fields as $field) {
if (is_array($field) && !empty($field['name']))
{
$ret[$field['name']] = $field;
}
}
}
}
}
return $ret;
}
protected function fieldIsRequired($def)
{
if (isset($def['studio']))
{
if (is_array($def['studio']))
{
if (!empty($def['studio'][$this->_view]) && $def['studio'][$this->_view] == "required")
{
return true;
}
else if (!empty($def['studio']['required']) && $def['studio']['required'] == true)
{
return true;
}
}
else if ($def['studio'] == "required" ){
return true;
}
}
return false;
}
static function _trimFieldDefs ( $def )
{
$ret = array_intersect_key ( $def ,
array ( 'studio' => true , 'name' => true , 'label' => true , 'displayParams' => true , 'comment' => true ,
'customCode' => true , 'customLabel' => true , 'tabIndex' => true , 'hideLabel' => true) ) ;
if (!empty($def['vname']) && empty($def['label']))
$ret['label'] = $def['vname'];
return $ret;
}
public function getUseTabs(){
if (isset($this->_viewdefs [ 'templateMeta' ]['useTabs']))
return $this->_viewdefs [ 'templateMeta' ]['useTabs'];
return false;
}
public function setUseTabs($useTabs){
$this->_viewdefs [ 'templateMeta' ]['useTabs'] = $useTabs;
}
}
?>

View File

@@ -0,0 +1,218 @@
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
require_once 'modules/ModuleBuilder/parsers/constants.php' ;
class History
{
private $_dirname ; // base directory for the history files
private $_basename ; // base name for a history file, for example, listviewdef.php
private $_list ; // the history - a list of history files
private $_previewFilename ; // the location of a file for preview
/*
* Constructor
* @param string $previewFilename The filename which the caller expects for a preview file
*/
function __construct ($previewFilename )
{
$GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->__construct( {$previewFilename} )" ) ;
$this->_previewFilename = $previewFilename ;
$this->_list = array ( ) ;
$this->_dirname = dirname ( $this->_previewFilename ) ;
// create the history directory if it does not already exist
if (! is_dir ( $this->_dirname ))
{
mkdir_recursive ( $this->_dirname ) ;
}
$this->_basename = basename ( $this->_previewFilename ) ;
// Reconstruct the history from the saved files
foreach ( scandir ( $this->_dirname ) as $filename )
{
if ($filename != "." && $filename != "..")
{
// history files are of the form {$basename}_{$timestamp}
if (preg_match ( '/(' . $this->_basename . ')_(.*)/', $filename, $matches ) == 1)
{
$this->_list [ $matches [ 2 ] ] = $matches [ 2 ] ;
}
}
}
// now sort the files, oldest first
if (count ( $this->_list ) > 0)
{
ksort ( $this->_list ) ;
}
}
/*
* Get the most recent item in the history
* @return timestamp of the first item
*/
function getCount ()
{
return count ( $this->_list ) ;
}
/*
* Get the most recent item in the history
* @return timestamp of the first item
*/
function getFirst ()
{
return end ( $this->_list ) ;
}
/*
* Get the oldest item in the history (the default layout)
* @return timestamp of the last item
*/
function getLast ()
{
return reset ( $this->_list ) ;
}
/*
* Get the next oldest item in the history
* @return timestamp of the next item
*/
function getNext ()
{
return prev ( $this->_list ) ;
}
/*
* Get the nth item in the history (where the zeroeth record is the most recent)
* @return timestamp of the nth item
*/
function getNth ($index)
{
$value = end ( $this->_list ) ;
$i = 0 ;
while ( $i < $index )
{
$value = prev ( $this->_list ) ;
$i ++ ;
}
return $value ;
}
/*
* Add an item to the history
* @return String A GMT Unix timestamp for this newly added item
*/
function append ($path)
{
// make sure we don't have a duplicate filename - highly unusual as two people should not be using Studio/MB concurrently, but when testing quite possible to do two appends within one second...
// because so unlikely in normal use we handle this the naive way by waiting a second so our naming scheme doesn't get overelaborated
$retries = 0 ;
$time = strtotime ( gmdate ( 'r' ) ) ;
while ( (file_exists ( $this->_previewFilename . "_" . $time ) && $retries < 5) )
{
sleep ( 1 ) ;
$time = strtotime ( gmdate ( 'r' ) ) ;
$retries ++ ;
}
// now we have a unique filename, copy the file into the history
copy ( $path, $this->_previewFilename . "_" . $time ) ;
$this->_list [ $time ] = $time ;
// finally, trim the number of files we're holding in the history to that specified in the configuration
$max_history = (isset ( $GLOBALS [ 'sugar_config' ] [ 'studio_max_history' ] )) ? $GLOBALS [ 'sugar_config' ] [ 'studio_max_history' ] : 50 ;
$count = count ( $this->_list ) ;
// truncate the oldest files, keeping only the most recent $GLOBALS['sugar_config']['studio_max_history'] files (zero=keep them all)
if (($max_history != 0) && ($count > $max_history))
{
// most recent files are at the end of the list, so we strip out the first count-max_history records
// can't just use array_shift because it renumbers numeric keys (our timestamp keys) to start from zero...
for ( $i = 0 ; $i < $count - $max_history ; $i ++ )
{
$timestamp = reset ( $this->_list ) ;
unset ( $this->_list [ $timestamp ] ) ;
if (! unlink ( $this->_dirname . "/" . $this->_basename . "_" . $timestamp ))
{
$GLOBALS [ 'log' ]->warn ( "History.php: unable to remove history file {$this->_basename}_$timestamp from directory {$this->_dirname} - permissions problem?" ) ;
}
}
}
// finally, remove any history preview file that might be lurking around - as soon as we append a new record it supercedes any old preview, so that must be removed (bug 20130)
if (file_exists($this->_previewFilename))
{
$GLOBALS [ 'log' ]->debug( get_class($this)."->append(): removing old history file at {$this->_previewFilename}");
unlink ( $this->_previewFilename);
}
return $time ;
}
/*
* Restore the historical layout identified by timestamp
* @param Unix timestamp $timestamp GMT Timestamp of the layout to recover
* @return GMT Timestamp if successful, null if failure (if the file could not be copied for some reason)
*/
function restoreByTimestamp ($timestamp)
{
$filename = $this->_previewFilename . "_" . $timestamp ;
$GLOBALS [ 'log' ]->debug ( get_class ( $this ) . ": restoring from $filename to {$this->_previewFilename}" ) ;
if (file_exists ( $filename ))
{
copy ( $filename, $this->_previewFilename ) ;
return $timestamp ;
}
return null ;
}
/*
* Undo the restore - revert back to the layout before the restore
*/
function undoRestore ()
{
if (file_exists ( $this->_previewFilename ))
{
unlink ( $this->_previewFilename ) ;
}
}
}

View File

@@ -0,0 +1,75 @@
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
interface HistoryInterface
{
/*
* Get the most recent item in the history
* @return Id of the first item
*/
function getFirst () ;
/*
* Get the next oldest item in the history
* @return Id of the next item
*/
function getNext () ;
/*
* Get the nth item in the history (where the zeroeth record is the most recent)
* @return Id of the nth item
*/
function getNth ($n) ;
/*
* Restore the historical layout identified by timestamp
* @return Timestamp if successful, null if failure (if the file could not be copied for some reason)
*/
function restoreByTimestamp ($timestamp) ;
/*
* Undo the restore - revert back to the layout before the restore
*/
function undoRestore () ;
/*
* Add an item to the history
* @return String An timestamp for this newly added item
*/
function append ($path) ;
}

View File

@@ -0,0 +1,361 @@
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
require_once 'modules/ModuleBuilder/parsers/views/AbstractMetaDataParser.php' ;
require_once 'modules/ModuleBuilder/parsers/views/MetaDataParserInterface.php' ;
class ListLayoutMetaDataParser extends AbstractMetaDataParser implements MetaDataParserInterface
{
// Columns is used by the view to construct the listview - each column is built by calling the named function
public $columns = array ( 'LBL_DEFAULT' => 'getDefaultFields' , 'LBL_AVAILABLE' => 'getAdditionalFields' , 'LBL_HIDDEN' => 'getAvailableFields' ) ;
protected $labelIdentifier = 'label' ; // labels in the listviewdefs.php are tagged 'label' =>
protected $allowParent = false;
/*
* Simple function for array_udiff_assoc function call in getAvailableFields()
*/
static function getArrayDiff ($one , $two)
{
$retArray = array();
foreach($one as $key => $value)
{
if (!isset($two[$key]))
{
$retArray[$key] = $value;
}
}
return $retArray;
}
/*
* Constructor
* @param string view The view type, that is, editview, searchview etc
* @param string moduleName The name of the module to which this listview belongs
* @param string packageName If not empty, the name of the package to which this listview belongs
*/
function __construct ($view , $moduleName , $packageName = '')
{
$GLOBALS [ 'log' ]->debug ( get_class ( $this ) . ": __construct()" ) ;
// BEGIN ASSERTIONS
$views = array ( MB_LISTVIEW, MB_DASHLET, MB_DASHLETSEARCH, MB_POPUPLIST, MB_POPUPSEARCH ) ;
if (! in_array ( $view , $views ) )
{
sugar_die ( "ListLayoutMetaDataParser: View $view is not supported" ) ;
}
// END ASSERTIONS
if (empty ( $packageName ))
{
require_once 'modules/ModuleBuilder/parsers/views/DeployedMetaDataImplementation.php' ;
$this->implementation = new DeployedMetaDataImplementation ( $view, $moduleName ) ;
} else
{
require_once 'modules/ModuleBuilder/parsers/views/UndeployedMetaDataImplementation.php' ;
$this->implementation = new UndeployedMetaDataImplementation ( $view, $moduleName, $packageName ) ;
}
$this->view = $view;
$this->_fielddefs = $this->implementation->getFielddefs () ;
$this->_standardizeFieldLabels( $this->_fielddefs );
$this->_viewdefs = array_change_key_case ( $this->implementation->getViewdefs () ) ; // force to lower case so don't have problems with case mismatches later
}
/*
* Deploy the layout
* @param boolean $populate If true (default), then update the layout first with new layout information from the $_REQUEST array
*/
function handleSave ($populate = true)
{
if ($populate)
$this->_populateFromRequest () ;
$this->implementation->deploy ( array_change_key_case ( $this->_viewdefs, CASE_UPPER ) ) ; // force the field names back to upper case so the list view will work correctly
}
function getLayout ()
{
return $this->_viewdefs ;
}
/**
* Return a list of the default fields for a listview
* @return array List of default fields as an array, where key = value = <field name>
*/
function getDefaultFields ()
{
$defaultFields = array ( ) ;
foreach ( $this->_viewdefs as $key => $def )
{
// add in the default fields from the listviewdefs but hide fields disabled in the listviewdefs.
if (! empty ( $def [ 'default' ] )
&& (!isset($def [ 'studio' ]) || ($def [ 'studio' ] !== false && $def [ 'studio' ] != "false")))
{
if (isset($this->_fielddefs [ $key ] )) {
$defaultFields [ $key ] = self::_trimFieldDefs ( $this->_fielddefs [ $key ] ) ;
if (!empty($def['label']))
$defaultFields [ $key ]['label'] = $def['label'];
}
else {
$defaultFields [ $key ] = $def;
}
}
}
return $defaultFields ;
}
/**
* Returns additional fields available for users to create fields
@return array List of additional fields as an array, where key = value = <field name>
*/
function getAdditionalFields ()
{
$additionalFields = array ( ) ;
foreach ( $this->_viewdefs as $key => $def )
{
//#25322
if(strtolower ( $key ) == 'email_opt_out'){
continue;
}
if (empty ( $def [ 'default' ] ))
{
if (isset($this->_fielddefs [ $key ] ))
$additionalFields [ $key ] = self::_trimFieldDefs ( $this->_fielddefs [ $key ] ) ;
else
$additionalFields [ $key ] = $def;
}
}
return $additionalFields ;
}
/**
* Returns unused fields that are available for use in either default or additional list views
* @return array List of available fields as an array, where key = value = <field name>
*/
function getAvailableFields ()
{
$availableFields = array ( ) ;
// Select available fields from the field definitions - don't need to worry about checking if ok to include as the Implementation has done that already in its constructor
foreach ( $this->_fielddefs as $key => $def )
{
if ($this->isValidField($key, $def))
$availableFields [ $key ] = self::_trimFieldDefs( $this->_fielddefs [ $key ] ) ;
$origDefs = $this->getOriginalViewDefs();
foreach($origDefs as $key => $def)
{
$availableFields [ $key ] = $def;
}
}
//$GLOBALS['log']->debug(get_class($this).'->getAvailableFields(): '.print_r($availableFields,true));
// now remove all fields that are already in the viewdef - they are not available; they are in use
return ListLayoutMetaDataParser::getArrayDiff ( $availableFields, $this->_viewdefs) ;
}
public function isValidField($key, $def)
{
//Studio invisible fields should always be hidden
if (! empty ($def[ 'studio' ] ) )
{
if (is_array($def [ 'studio' ]))
{
if (isset($def [ 'studio' ]['listview']))
return $def [ 'studio' ]['listview'] !== false && $def [ 'studio' ]['listview'] != 'false';
if (isset($def [ 'studio' ]['visible']))
return $def [ 'studio' ]['visible'];
} else
{
return ($def [ 'studio' ] != 'false' && $def [ 'studio' ] !== false && $def [ 'studio' ] != 'hidden') ;
}
}
//Bug 32520. We need to dissalow currency_id fields on list views.
//This should be removed once array based studio definitions are in.
if (isset($def['type']) && $def['type'] == "id" && $def['name'] == 'currency_id')
return false;
//Check fields types
if (isset($def['dbType']) && $def['dbType'] == "id")
{
return false;
}
if (isset($def['type']))
{
if ($def['type'] == 'html' || ($def['type'] == 'parent' && !$this->allowParent)
|| $def['type'] == "id" || $def['type'] == "link" || $def['type'] == 'image')
return false;
}
//hide currency_id, deleted, and _name fields by key-name
if(strcmp ( $key, 'deleted' ) == 0 ) {
return false;
}
//if all the tests failed, the field is probably ok
return true;
}
protected function _populateFromRequest ()
{
$GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->populateFromRequest() - fielddefs = ".print_r($this->_fielddefs, true));
// Transfer across any reserved fields, that is, any where studio !== true, which are not editable but must be preserved
$newViewdefs = array ( ) ;
$rejectTypes = array ( 'html'=>'html' , 'enum'=>'enum' , 'text'=>'text', 'encrypt'=>'encrypt' ) ;
$originalViewDefs = $this->getOriginalViewDefs();
foreach ( $this->_viewdefs as $key => $def )
{
//If the field is on the layout, but studio disabled, put it back on the layout at the front
if (isset ($def['studio']) && (
(is_array($def['studio']) && isset($def['studio']['listview']) &&
($def['studio']['listview'] === false || strtolower($def['studio']['listview']) == 'false'
|| strtolower($def['studio']['listview']) == 'required')
)
|| (!is_array($def['studio']) &&
($def [ 'studio' ] === false || strtolower($def['studio']) == 'false' || strtolower($def['studio']) == 'required'))
))
{
$newViewdefs [ $key ] = $def ;
}
}
// only take items from group_0 for searchviews (basic_search or advanced_search) and subpanels (which both are missing the Available column) - take group_0, _1 and _2 for all other list views
$lastGroup = (isset ( $this->columns [ 'LBL_AVAILABLE' ] )) ? 2 : 1 ;
for ( $i = 0 ; isset ( $_POST [ 'group_' . $i ] ) && $i < $lastGroup ; $i ++ )
{
foreach ( $_POST [ 'group_' . $i ] as $fieldname )
{
$fieldname = strtolower ( $fieldname ) ;
//Check if the field was previously on the layout
if (isset ($this->_viewdefs[$fieldname])) {
$newViewdefs [ $fieldname ] = $this->_viewdefs[$fieldname];
// print_r($this->_viewdefs[ $fieldname ]);
}
//Next check if the original view def contained it
else if (isset($originalViewDefs[ $fieldname ]))
{
$newViewdefs [ $fieldname ] = $originalViewDefs[ $fieldname ];
}
//create a definition from the fielddefs
else
{
// if we don't have a valid fieldname then just ignore it and move on...
if ( ! isset ( $this->_fielddefs [ $fieldname ] ) )
continue ;
$newViewdefs [ $fieldname ] = $this->_trimFieldDefs($this->_fielddefs [ $fieldname ]) ;
// sorting fields of certain types will cause a database engine problems
if ( isset($this->_fielddefs[$fieldname]['type']) &&
isset ( $rejectTypes [ $this->_fielddefs [ $fieldname ] [ 'type' ] ] ))
{
$newViewdefs [ $fieldname ] [ 'sortable' ] = false ;
}
// Bug 23728 - Make adding a currency type field default to setting the 'currency_format' to true
if (isset ( $this->_fielddefs [ $fieldname ] [ 'type' ]) && $this->_fielddefs [ $fieldname ] [ 'type' ] == 'currency')
{
$newViewdefs [ $fieldname ] [ 'currency_format' ] = true;
}
}
if (isset ( $_REQUEST [ strtolower ( $fieldname ) . 'width' ] ))
{
$width = substr ( $_REQUEST [ $fieldname . 'width' ], 6, 3 ) ;
if (strpos ( $width, "%" ) != false)
{
$width = substr ( $width, 0, 2 ) ;
}
if (!($width < 101 && $width > 0))
{
$width = 10;
}
$newViewdefs [ $fieldname ] [ 'width' ] = $width."%" ;
} else if (isset ( $this->_viewdefs [ $fieldname ] [ 'width' ] ))
{
$newViewdefs [ $fieldname ] [ 'width' ] = $this->_viewdefs [ $fieldname ] [ 'width' ] ;
}
else {
$newViewdefs [ $fieldname ] [ 'width' ] = "10%";
}
$newViewdefs [ $fieldname ] [ 'default' ] = ($i == 0) ;
}
}
$this->_viewdefs = $newViewdefs ;
}
/*
* Remove all instances of a field from the layout
* @param string $fieldName Name of the field to remove
* @return boolean True if the field was removed; false otherwise
*/
function removeField ($fieldName)
{
if (isset ( $this->_viewdefs [ $fieldName ] ))
{
unset( $this->_viewdefs [ $fieldName ] ) ;
return true ;
}
return false ;
}
function getOriginalViewDefs() {
$defs = $this->implementation->getOriginalViewdefs ();
$out = array();
foreach ($defs as $field => $def)
{
$out[strtolower($field)] = $def;
}
return $out;
}
static function _trimFieldDefs ( $def )
{
if ( isset ( $def [ 'vname' ] ) )
$def [ 'label' ] = $def [ 'vname' ] ;
return array_intersect_key ( $def , array ( 'type' => true, 'studio' => true , 'label' => true , 'width' => true , 'sortable' => true , 'related_fields' => true , 'default' => true , 'link' => true , 'align' => true , 'orderBy' => true ,'hideLabel' => true, 'customLable' => true , 'currency_format' => true ) ) ;
}
}

View File

@@ -0,0 +1,45 @@
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
interface MetaDataImplementationInterface
{
public function getViewdefs () ;
public function getFielddefs () ;
public function getLanguage () ;
public function deploy ($defs) ;
public function getHistory () ;
}

View File

@@ -0,0 +1,50 @@
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
interface MetaDataParserInterface
{
public function handleSave () ;
public function getLayout () ;
public function getLanguage () ;
public function getHistory () ;
}
?>

View File

@@ -0,0 +1,233 @@
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
require_once ('modules/ModuleBuilder/parsers/views/ListLayoutMetaDataParser.php') ;
require_once ('modules/ModuleBuilder/parsers/views/SearchViewMetaDataParser.php') ;
require_once 'modules/ModuleBuilder/parsers/constants.php' ;
class PopupMetaDataParser extends ListLayoutMetaDataParser
{
// Columns is used by the view to construct the listview - each column is built by calling the named function
public $columns = array ( 'LBL_DEFAULT' => 'getDefaultFields' , 'LBL_AVAILABLE' => 'getAdditionalFields' , 'LBL_HIDDEN' => 'getAvailableFields' ) ;
public static $reserveProperties = array('moduleMain', 'varName' , 'orderBy', 'whereClauses', 'searchInputs', 'create');
public static $defsMap = array(MB_POPUPSEARCH => 'searchdefs' , MB_POPUPLIST => 'listviewdefs');
/*
* Constructor
* Must set:
* $this->columns Array of 'Column LBL'=>function_to_retrieve_fields_for_this_column() - expected by the view
*
* @param string moduleName The name of the module to which this listview belongs
* @param string packageName If not empty, the name of the package to which this listview belongs
*/
function __construct ($view, $moduleName , $packageName = '')
{
$this->search = ($view == MB_POPUPSEARCH) ? true : false;
$this->_moduleName = $moduleName;
$this->_packageName = $packageName;
$this->_view = $view ;
$this->columns = array ( 'LBL_DEFAULT' => 'getDefaultFields' , 'LBL_HIDDEN' => 'getAvailableFields' ) ;
if ($this->search)
{
$this->columns = array ( 'LBL_DEFAULT' => 'getSearchFields' , 'LBL_HIDDEN' => 'getAvailableFields' ) ;
parent::__construct ( MB_POPUPSEARCH, $moduleName, $packageName ) ;
} else
{
parent::__construct ( MB_POPUPLIST, $moduleName, $packageName ) ;
}
$this->_viewdefs = $this->mergeFieldDefinitions($this->_viewdefs, $this->_fielddefs);
}
/**
* Dashlets contain both a searchview and list view definition, therefore we need to merge only the relevant info
*/
function mergeFieldDefinitions ( $viewdefs, $fielddefs ) {
$viewdefs = $this->_viewdefs = array_change_key_case($viewdefs );
$viewdefs = $this->_viewdefs = $this->convertSearchToListDefs($viewdefs);
return $viewdefs;
}
function convertSearchToListDefs($defs) {
$temp = array();
foreach($defs as $key=>$value) {
if(!is_array($value)){
$temp[$value] = array('name'=>$value);
}else{
$temp[$key] = $value;
if(isset($value['name']) && $value['name'] != $key){
$temp[$value['name']] = $value;
unset($temp[$key] );
}else if( !isset($value['name']) ){
$temp[$key]['name'] = $key;
}
}
}
return $temp;
}
function getOriginalViewDefs(){
$defs = parent::getOriginalViewDefs();
return $this->convertSearchToListDefs($defs);
}
public function getSearchFields()
{
$searchFields = array ( ) ;
foreach ( $this->_viewdefs as $key => $def )
{
if (isset($this->_fielddefs [ $key ] )) {
$searchFields [ $key ] = self::_trimFieldDefs ( $this->_fielddefs [ $key ] ) ;
if (!empty($def['label']))
$searchFields [ $key ]['label'] = $def['label'];
}
else {
$searchFields [ $key ] = $def;
}
}
return $searchFields ;
}
function handleSave ($populate = true)
{
if (empty ( $this->_packageName ))
{
foreach(array(MB_CUSTOMMETADATALOCATION , MB_BASEMETADATALOCATION) as $value){
$file = $this->implementation->getFileName(MB_POPUPLIST, $this->_moduleName, $value);
if(file_exists($file)){
break;
}
}
$writeFile = $this->implementation->getFileName(MB_POPUPLIST, $this->_moduleName);
if(!file_exists($writeFile)){
mkdir_recursive ( dirname ( $writeFile ) ) ;
}
}
else{
$writeFile = $file = $this->implementation->getFileName(MB_POPUPLIST, $this->_moduleName, $this->_packageName);
}
$this->implementation->_history->append ( $file ) ;
if ($populate)
$this->_populateFromRequest() ;
$out = "<?php\n" ;
//Load current module languages
global $mod_strings , $current_language;
$oldModStrings = $mod_strings;
$GLOBALS['mod_strings'] = return_module_language($current_language , $this->_moduleName);
require($file);
if (!isset($popupMeta)) {
sugar_die ("unable to load Module Popup Definition");
}
if ($this->_view == MB_POPUPSEARCH)
{
foreach($this->_viewdefs as $k => $v){
if(isset($this->_viewdefs[$k]) && isset($this->_viewdefs[$k]['default'])){
unset($this->_viewdefs[$k]['default']);
}
}
$this->_viewdefs = $this->convertSearchToListDefs($this->_viewdefs);
$popupMeta['searchdefs'] = $this->_viewdefs;
$this->addNewSearchDef($this->_viewdefs , $popupMeta);
} else
{
$popupMeta['listviewdefs'] = array_change_key_case($this->_viewdefs , CASE_UPPER );
}
$allProperties = array_merge(self::$reserveProperties , array('searchdefs', 'listviewdefs'));
$out .= "\$popupMeta = array (\n";
foreach( $allProperties as $p){
if(isset($popupMeta[$p])){
$out .= " '$p' => ". var_export_helper ($popupMeta[$p]) . ",\n";
}
}
$out .= ");\n";
file_put_contents($writeFile, $out);
//return back mod strings
$GLOBALS['mod_strings'] = $oldModStrings;
}
public function addNewSearchDef($searchDefs, &$popupMeta){
if(!empty($searchDefs)){
$this->__diffAndUpdate( $searchDefs , $popupMeta['whereClauses'] , true);
$this->__diffAndUpdate( $searchDefs , $popupMeta['searchInputs'] );
}
}
private function __diffAndUpdate($newDefs , &$targetDefs , $forWhere = false){
if(!is_array($targetDefs)){
$targetDefs = array();
}
foreach($newDefs as $key =>$def){
if(!isset($targetDefs[$key]) && $forWhere){
$targetDefs[$key] = $this->__getTargetModuleName($def).'.'.$key;
}else if( !in_array($key , $targetDefs ) && !$forWhere){
array_push($targetDefs , $key);
}
}
if($forWhere){
foreach(array_diff( array_keys($targetDefs) , array_keys($newDefs) ) as $key ){
unset($targetDefs[$key]);
}
}else{
foreach($targetDefs as $key =>$value){
if(!isset($newDefs[$value]))
unset($targetDefs[$key]);
}
}
}
private function __getTargetModuleName($def){
$dir = strtolower($this->implementation->getModuleDir());
if(isset($this->_fielddefs[$def['name']]) && isset($this->_fielddefs[$def['name']]['source']) && $this->_fielddefs[$def['name']]['source'] == 'custom_fields'){
return $dir.'_cstm';
}
return $dir;
}
}
?>

View File

@@ -0,0 +1,200 @@
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
require_once ('modules/ModuleBuilder/parsers/views/ListLayoutMetaDataParser.php') ;
require_once 'modules/ModuleBuilder/parsers/constants.php' ;
class SearchViewMetaDataParser extends ListLayoutMetaDataParser
{
static $variableMap = array (
MB_BASICSEARCH => 'basic_search' ,
MB_ADVANCEDSEARCH => 'advanced_search' ,
) ;
// Columns is used by the view to construct the listview - each column is built by calling the named function
public $columns = array ( 'LBL_DEFAULT' => 'getDefaultFields' , 'LBL_HIDDEN' => 'getAvailableFields' ) ;
protected $allowParent = true;
/*
* Constructor
* Must set:
* $this->columns Array of 'Column LBL'=>function_to_retrieve_fields_for_this_column() - expected by the view
* @param string searchLayout The type of search layout, e.g., MB_BASICSEARCH or MB_ADVANCEDSEARCH
* @param string moduleName The name of the module to which this listview belongs
* @param string packageName If not empty, the name of the package to which this listview belongs
*/
function __construct ($searchLayout, $moduleName , $packageName = '')
{
$GLOBALS [ 'log' ]->debug ( get_class ( $this ) . ": __construct( $searchLayout , $moduleName , $packageName )" ) ;
// BEGIN ASSERTIONS
if (! isset ( self::$variableMap [ $searchLayout ] ) )
{
sugar_die ( get_class ( $this ) . ": View $searchLayout is not supported" ) ;
}
// END ASSERTIONS
$this->_searchLayout = $searchLayout ;
// unsophisticated error handling for now...
try
{
if (empty ( $packageName ))
{
require_once 'modules/ModuleBuilder/parsers/views/DeployedMetaDataImplementation.php' ;
$this->implementation = new DeployedMetaDataImplementation ( $searchLayout, $moduleName ) ;
} else
{
require_once 'modules/ModuleBuilder/parsers/views/UndeployedMetaDataImplementation.php' ;
$this->implementation = new UndeployedMetaDataImplementation ( $searchLayout, $moduleName, $packageName ) ;
}
} catch (Exception $e)
{
throw $e ;
}
$this->_saved = array_change_key_case ( $this->implementation->getViewdefs () ) ; // force to lower case so don't have problems with case mismatches later
if(isset($this->_saved['templatemeta'])) {
$this->_saved['templateMeta'] = $this->_saved['templatemeta'];
unset($this->_saved['templatemeta']);
}
if ( ! isset ( $this->_saved [ 'layout' ] [ self::$variableMap [ $this->_searchLayout ] ] ) )
{
// attempt to fallback on a basic_search layout...
if ( ! isset ( $this->_saved [ 'layout' ] [ self::$variableMap [ MB_BASICSEARCH ] ] ) )
throw new Exception ( get_class ( $this ) . ": {$this->_searchLayout} does not exist for module $moduleName" ) ;
$this->_saved [ 'layout'] [ MB_ADVANCEDSEARCH ] = $this->_saved [ 'layout' ] [ MB_BASICSEARCH ] ;
}
$this->view = $searchLayout;
// convert the search view layout (which has its own unique layout form) to the standard listview layout so that the parser methods and views can be reused
$this->_viewdefs = $this->convertSearchViewToListView ( $this->_saved [ 'layout' ] [ self::$variableMap [ $this->_searchLayout ] ] ) ;
$this->_fielddefs = $this->implementation->getFielddefs () ;
$this->_standardizeFieldLabels( $this->_fielddefs );
}
public function isValidField($key, $def)
{
if (!parent::isValidField($key, $def))
return false;
if (isset($def [ 'studio' ]) && is_array($def [ 'studio' ]) && isset($def [ 'studio' ]['searchview']))
{
return $def [ 'studio' ]['searchview'] !== false && $def [ 'studio' ]['searchview'] != 'false';
}
//Special case to prevent multiple copies of assigned user on the search view
if (empty ($def[ 'studio' ] ) && $key == "assigned_user_name" )
{
$origDefs = $this->getOriginalViewDefs();
if (isset($origDefs['assigned_user_id']))
return false;
}
//Remove image fields (unless studio was set)
if (!empty($def [ 'studio' ]) && isset($def['type']) && $def['type'] == "image")
return false;
return true;
}
/*
* Save the modified searchLayout
* Have to preserve the original layout format, which is array('metadata'=>array,'layouts'=>array('basic'=>array,'advanced'=>array))
*/
function handleSave ($populate = true)
{
if ($populate)
$this->_populateFromRequest() ;
$this->_saved [ 'layout' ] [ self::$variableMap [ $this->_searchLayout ] ] = $this->convertSearchViewToListView($this->_viewdefs);;
$this->implementation->deploy ( $this->_saved ) ;
}
private function convertSearchViewToListView ($viewdefs)
{
$temp = array ( ) ;
foreach ( $viewdefs as $key => $value )
{
if (! is_array ( $value ))
{
$key = $value ;
$def = array ( ) ;
$def[ 'name' ] = $key;
$value = $def ;
}
if (!isset ( $value [ 'name' ] ))
{
$value [ 'name' ] = $key;
}
else
{
$key = $value [ 'name' ] ; // override key with name, needed when the entry lacks a key
}
// now add in the standard listview default=>true
$value [ 'default' ] = true ;
$temp [ strtolower ( $key ) ] = $value ;
}
return $temp ;
}
function getOriginalViewDefs() {
$defs = $this->implementation->getOriginalViewdefs ();
$out = array();
if (!empty($defs) && !empty($defs['layout']) && !empty($defs['layout'][$this->_searchLayout]))
{
$defs = $defs['layout'][$this->_searchLayout];
foreach ($defs as $def)
{
if (is_array($def) && isset($def['name']))
{
$out[strtolower($def['name'])] = $def;
}
}
}
return $out;
}
}
?>

View File

@@ -0,0 +1,161 @@
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
require_once ('modules/ModuleBuilder/parsers/views/ListLayoutMetaDataParser.php') ;
require_once 'modules/ModuleBuilder/parsers/constants.php' ;
class SubpanelMetaDataParser extends ListLayoutMetaDataParser
{
// Columns is used by the view to construct the listview - each column is built by calling the named function
public $columns = array ( 'LBL_DEFAULT' => 'getDefaultFields' , 'LBL_HIDDEN' => 'getAvailableFields' ) ;
protected $labelIdentifier = 'vname' ; // labels in the subpanel defs are tagged 'vname' =>
/*
* Constructor
* Must set:
* $this->columns Array of 'Column LBL'=>function_to_retrieve_fields_for_this_column() - expected by the view
*
* @param string subpanelName The name of this subpanel
* @param string moduleName The name of the module to which this subpanel belongs
* @param string packageName If not empty, the name of the package to which this subpanel belongs
*/
function __construct ($subpanelName , $moduleName , $packageName = '')
{
$GLOBALS [ 'log' ]->debug ( get_class ( $this ) . ": __construct()" ) ;
// TODO: check the implementations
if (empty ( $packageName ))
{
require_once 'modules/ModuleBuilder/parsers/views/DeployedSubpanelImplementation.php' ;
$this->implementation = new DeployedSubpanelImplementation ( $subpanelName, $moduleName ) ;
//$this->originalViewDef = $this->implementation->getOriginalDefs ();
} else
{
require_once 'modules/ModuleBuilder/parsers/views/UndeployedSubpanelImplementation.php' ;
$this->implementation = new UndeployedSubpanelImplementation ( $subpanelName, $moduleName, $packageName ) ;
}
$this->_viewdefs = array_change_key_case ( $this->implementation->getViewdefs () ) ; // force to lower case so don't have problems with case mismatches later
$this->_fielddefs = $this->implementation->getFielddefs ();
$this->_standardizeFieldLabels( $this->_fielddefs );
$GLOBALS['log']->debug ( get_class($this)."->__construct(): viewdefs = ".print_r($this->_viewdefs,true));
$GLOBALS['log']->debug ( get_class($this)."->__construct(): viewdefs = ".print_r($this->_viewdefs,true));
$this->_invisibleFields = $this->findInvisibleFields( $this->_viewdefs ) ;
$GLOBALS['log']->debug ( get_class($this)."->__construct(): invisibleFields = ".print_r($this->_invisibleFields,true));
}
/*
* Save the layout
*/
function handleSave ($populate = true)
{
if ($populate)
{
$this->_populateFromRequest() ;
if (isset ($_REQUEST['subpanel_title']) && isset($_REQUEST['subpanel_title_key'])) {
$selected_lang = (!empty($_REQUEST['selected_lang'])? $_REQUEST['selected_lang']:$_SESSION['authenticated_user_language']);
if(empty($selected_lang)){
$selected_lang = $GLOBALS['sugar_config']['default_language'];
}
require_once 'modules/ModuleBuilder/parsers/parser.label.php' ;
$labelParser = new ParserLabel ( $_REQUEST['view_module'] , isset ( $_REQUEST [ 'view_package' ] ) ? $_REQUEST [ 'view_package' ] : null ) ;
$labelParser->addLabels($selected_lang, array($_REQUEST['subpanel_title_key'] => $_REQUEST['subpanel_title']), $_REQUEST['view_module']);
}
}
$this->implementation->deploy ( $this->restoreInvisibleFields ( $this->_invisibleFields , $this->_viewdefs ) ) ; // unlike our parent, do not force the field names back to upper case
}
/**
* Return a list of the default fields for a subpanel
* TODO: have this return just a list of fields, without definitions
* @return array List of default fields as an array, where key = value = <field name>
*/
function getDefaultFields ()
{
$defaultFields = array ( ) ;
foreach ( $this->_viewdefs as $key => $def )
{
if (empty ( $def [ 'usage' ] ) || strcmp ( $def [ 'usage' ], 'query_only' ) == 1)
{
$defaultFields [ strtolower ( $key ) ] = $this->_viewdefs [ $key ] ;
}
}
return $defaultFields ;
}
/*
* Find the query_only fields in the viewdefs
* Query_only fields are used by the MVC to generate the subpanel but are not editable - they must be maintained in the layout
* @param viewdefs The viewdefs to be searched for invisible fields
* @return Array of invisible fields, ready to be provided to $this->restoreInvisibleFields
*/
function findInvisibleFields( $viewdefs )
{
$invisibleFields = array () ;
foreach ( $viewdefs as $name => $def )
if ( isset($def [ 'usage' ] ) && ($def [ 'usage'] == 'query_only') )
$invisibleFields [ $name ] = $def ;
return $invisibleFields ;
}
function restoreInvisibleFields ( $invisibleFields , $viewdefs )
{
foreach ( $invisibleFields as $name => $def )
{
$viewdefs [ $name ] = $def ;
}
return $viewdefs ;
}
static function _trimFieldDefs ( $def )
{
$listDef = parent::_trimFieldDefs($def);
if (isset($listDef ['label']))
{
$listDef ['vname'] = $listDef ['label'];
unset($listDef ['label']);
}
return $listDef;
}
}
?>

View File

@@ -0,0 +1,206 @@
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
require_once 'modules/ModuleBuilder/MB/ModuleBuilder.php' ;
require_once 'modules/ModuleBuilder/parsers/views/AbstractMetaDataImplementation.php' ;
require_once 'modules/ModuleBuilder/parsers/views/MetaDataImplementationInterface.php' ;
require_once 'modules/ModuleBuilder/parsers/views/ListLayoutMetaDataParser.php' ;
require_once 'modules/ModuleBuilder/parsers/views/GridLayoutMetaDataParser.php' ;
require_once 'modules/ModuleBuilder/parsers/constants.php' ;
class UndeployedMetaDataImplementation extends AbstractMetaDataImplementation implements MetaDataImplementationInterface
{
private $_packageName ;
/*
* Constructor
* @param string $view
* @param string $moduleName
* @throws Exception Thrown if the provided view doesn't exist for this module
*/
function __construct ($view , $moduleName , $packageName)
{
// BEGIN ASSERTIONS
if (! isset ( $this->_fileVariables [ $view ] ))
{
sugar_die ( get_class ( $this ) . ": View $view is not supported" ) ;
}
// END ASSERTIONS
$this->_view = strtolower ( $view ) ;
$this->_moduleName = $moduleName ;
$this->_packageName = $packageName ;
//get the bean from ModuleBuilder
$mb = new ModuleBuilder ( ) ;
$this->module = $module = & $mb->getPackageModule ( $packageName, $moduleName ) ;
$pak = $mb->getPackage($packageName);
$module->mbvardefs->updateVardefs () ;
// Set the list of fields associated with this module
$fielddefs = array_change_key_case ( $module->mbvardefs->vardefs [ 'fields' ] ) ;
// Set the global mod_strings directly as Sugar does not automatically load the language files for undeployed modules (how could it?)
$GLOBALS [ 'mod_strings' ] = array_merge ( $GLOBALS [ 'mod_strings' ], $module->getModStrings () ) ;
//Load relationshhip based fields and labels
$moduleRels = $pak->getRelationshipsForModule($moduleName);
foreach($moduleRels as $rName => $rel ) {
$varDefsSet = $rel->buildVardefs();
if (!empty($varDefsSet[$module->key_name])) {
foreach ($varDefsSet[$module->key_name] as $def) {
$fielddefs[$def['name']] = $def;
}
}
$labels = $rel->buildLabels();
foreach ($labels as $def) {
if ($def['module'] == $module->key_name) {
$GLOBALS [ 'mod_strings' ][$def['system_label']] = $def['display_label'];
}
}
}
$loaded = null ;
foreach ( array ( MB_WORKINGMETADATALOCATION , MB_HISTORYMETADATALOCATION ) as $type )
{
$this->_sourceFilename = $this->getFileName ( $view, $moduleName, $packageName , $type ) ;
if($view == MB_POPUPSEARCH || $view == MB_POPUPLIST){
$layout = $this->_loadFromPopupFile ( $this->_sourceFilename , null, $view);
}else{
$layout = $this->_loadFromFile ( $this->_sourceFilename );
}
if ( null !== $layout )
{
// merge in the fielddefs from this layout
$this->_mergeFielddefs ( $fielddefs , $layout ) ;
$loaded = $layout ;
}
}
if ($loaded === null)
{
throw new Exception ( get_class ( $this ) . ": view definitions for View $this->_view and Module $this->_moduleName are missing" ) ;
}
$this->_viewdefs = $loaded ;
$sourceFilename = $this->getFileName ( $view, $moduleName, $packageName, MB_WORKINGMETADATALOCATION );
if($view == MB_POPUPSEARCH || $view == MB_POPUPLIST){
$layout = $this->_loadFromPopupFile ( $sourceFilename , null, $view);
}else{
$layout = $this->_loadFromFile ($sourceFilename) ;
}
$this->_originalViewdefs = $layout ;
$this->_fielddefs = $fielddefs ;
$this->_history = new History ( $this->getFileName ( $view, $moduleName, $packageName, MB_HISTORYMETADATALOCATION ) ) ;
}
function getLanguage ()
{
return $this->_packageName . $this->_moduleName ;
}
/*
* Deploy a layout
* @param array defs Layout definition in the same format as received by the constructor
*/
function deploy ($defs)
{
//If we are pulling from the History Location, that means we did a restore, and we need to save the history for the previous file.
if ($this->_sourceFilename == $this->getFileName ( $this->_view, $this->_moduleName, $this->_packageName, MB_HISTORYMETADATALOCATION )
&& file_exists($this->getFileName ( $this->_view, $this->_moduleName, $this->_packageName, MB_WORKINGMETADATALOCATION ))) {
$this->_history->append ( $this->getFileName ( $this->_view, $this->_moduleName, $this->_packageName, MB_WORKINGMETADATALOCATION )) ;
} else {
$this->_history->append ( $this->_sourceFilename ) ;
}
$filename = $this->getFileName ( $this->_view, $this->_moduleName, $this->_packageName, MB_WORKINGMETADATALOCATION ) ;
$GLOBALS [ 'log' ]->debug ( get_class ( $this ) . "->deploy(): writing to " . $filename ) ;
$this->_saveToFile ( $filename, $defs ) ;
}
/*
* Construct a full pathname for the requested metadata
* @param string view The view type, that is, EditView, DetailView etc
* @param string modulename The name of the module that will use this layout
* @param string type
*/
public static function getFileName ($view , $moduleName , $packageName , $type = MB_BASEMETADATALOCATION)
{
$type = strtolower ( $type ) ;
// BEGIN ASSERTIONS
if ($type != MB_BASEMETADATALOCATION && $type != MB_HISTORYMETADATALOCATION)
{
// just warn rather than die
$GLOBALS [ 'log' ]->warning ( "UndeployedMetaDataImplementation->getFileName(): view type $type is not recognized" ) ;
}
// END ASSERTIONS
$filenames = array ( MB_DASHLETSEARCH => 'dashletviewdefs',
MB_DASHLET => 'dashletviewdefs',
MB_LISTVIEW => 'listviewdefs' ,
MB_BASICSEARCH => 'searchdefs' ,
MB_ADVANCEDSEARCH => 'searchdefs' ,
MB_EDITVIEW => 'editviewdefs' ,
MB_DETAILVIEW => 'detailviewdefs' ,
MB_QUICKCREATE => 'quickcreatedefs',
MB_POPUPSEARCH => 'popupdefs',
MB_POPUPLIST => 'popupdefs',
) ;
switch ( $type)
{
case MB_HISTORYMETADATALOCATION :
return 'custom/history/modulebuilder/packages/' . $packageName . '/modules/' . $moduleName . '/metadata/' . $filenames [ $view ] . '.php' ;
default :
// get the module again, all so we can call this method statically without relying on the module stored in the class variables
$mb = new ModuleBuilder ( ) ;
$module = & $mb->getPackageModule ( $packageName, $moduleName ) ;
return $module->getModuleDir () . '/metadata/' . $filenames [ $view ] . '.php' ;
}
}
public function getModuleDir(){
return $this->module->key_name;
}
}
?>

View File

@@ -0,0 +1,109 @@
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
/*********************************************************************************
* SugarCRM is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2010 SugarCRM Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along with
* this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "Powered by
* SugarCRM" logo. If the display of the logo is not reasonably feasible for
* technical reasons, the Appropriate Legal Notices must display the words
* "Powered by SugarCRM".
********************************************************************************/
/*
* Changes to AbstractSubpanelImplementation for DeployedSubpanels
* The main differences are in the load and save of the definitions
* For subpanels we must make use of the SubPanelDefinitions class to do this; this also means that the history mechanism,
* which tracks files, not objects, needs us to create an intermediate file representation of the definition that it can manage and restore
*/
require_once 'modules/ModuleBuilder/parsers/views/MetaDataImplementationInterface.php' ;
require_once 'modules/ModuleBuilder/parsers/views/AbstractMetaDataImplementation.php' ;
require_once 'modules/ModuleBuilder/parsers/constants.php' ;
class UndeployedSubpanelImplementation extends AbstractMetaDataImplementation implements MetaDataImplementationInterface
{
const HISTORYFILENAME = 'restored.php' ;
const HISTORYVARIABLENAME = 'layout_defs' ;
/*
* Constructor
* @param string subpanelName The name of this subpanel
* @param string moduleName The name of the module to which this subpanel belongs
* @param string packageName If not empty, the name of the package to which this subpanel belongs
*/
function __construct ($subpanelName , $moduleName , $packageName)
{
$this->_subpanelName = $subpanelName ;
$this->_moduleName = $moduleName ;
// TODO: history
$this->historyPathname = 'custom/history/modulebuilder/packages/' . $packageName . '/modules/' . $moduleName . '/metadata/' . self::HISTORYFILENAME ;
$this->_history = new History ( $this->historyPathname ) ;
//get the bean from ModuleBuilder
$mb = new ModuleBuilder ( ) ;
$this->module = & $mb->getPackageModule ( $packageName, $moduleName ) ;
$this->module->mbvardefs->updateVardefs () ;
$this->_fielddefs = & $this->module->mbvardefs->vardefs [ 'fields' ] ;
$subpanel_layout = $this->module->getAvailibleSubpanelDef ( $this->_subpanelName ) ;
$this->_viewdefs = & $subpanel_layout [ 'list_fields' ] ;
$this->_mergeFielddefs($this->_fielddefs, $this->_viewdefs);
// Set the global mod_strings directly as Sugar does not automatically load the language files for undeployed modules (how could it?)
$GLOBALS [ 'mod_strings' ] = array_merge ( $GLOBALS [ 'mod_strings' ], $this->module->getModStrings () ) ;
}
function getLanguage ()
{
return "" ; // '' is the signal to translate() to use the global mod_strings
}
/*
* Save a subpanel
* @param array defs Layout definition in the same format as received by the constructor
* @param string type The location for the file - for example, MB_BASEMETADATALOCATION for a location in the OOB metadata directory
*/
function deploy ($defs)
{
$outputDefs = $this->module->getAvailibleSubpanelDef ( $this->_subpanelName ) ;
// first sort out the historical record...
// copy the definition to a temporary file then let the history object add it
write_array_to_file ( self::HISTORYVARIABLENAME, $outputDefs, $this->historyPathname, 'w', '' ) ;
$this->_history->append ( $this->historyPathname ) ;
// no need to unlink the temporary file as being handled by in history->append()
//unlink ( $this->historyPathname ) ;
$outputDefs [ 'list_fields' ] = $defs ;
$this->_viewdefs = $defs ;
$this->module->saveAvailibleSubpanelDef ( $this->_subpanelName, $outputDefs ) ;
}
}