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,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 ) ;
}
}