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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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