/data/SugarBean.php
PHP | 5467 lines | 3745 code | 509 blank | 1213 comment | 975 complexity | 7fb508b71a67ee4f25b80dc4502e84b5 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, AGPL-3.0, LGPL-2.1
Large files files are truncated, but you can click here to view the full file
- <?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-2011 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".
- ********************************************************************************/
- /*********************************************************************************
- * Description: Defines the base class for all data entities used throughout the
- * application. The base class including its methods and variables is designed to
- * be overloaded with module-specific methods and variables particular to the
- * module's base entity class.
- * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc.
- * All Rights Reserved.
- *******************************************************************************/
- require_once('modules/DynamicFields/DynamicField.php');
- /**
- * SugarBean is the base class for all business objects in Sugar. It implements
- * the primary functionality needed for manipulating business objects: create,
- * retrieve, update, delete. It allows for searching and retrieving list of records.
- * It allows for retrieving related objects (e.g. contacts related to a specific account).
- *
- * In the current implementation, there can only be one bean per folder.
- * Naming convention has the bean name be the same as the module and folder name.
- * All bean names should be singular (e.g. Contact). The primary table name for
- * a bean should be plural (e.g. contacts).
- *
- */
- class SugarBean
- {
- /**
- * A pointer to the database helper object DBHelper
- *
- * @var DBHelper
- */
- var $db;
- /**
- * When createing a bean, you can specify a value in the id column as
- * long as that value is unique. During save, if the system finds an
- * id, it assumes it is an update. Setting new_with_id to true will
- * make sure the system performs an insert instead of an update.
- *
- * @var BOOL -- default false
- */
- var $new_with_id = false;
- /**
- * Disble vardefs. This should be set to true only for beans that do not have varders. Tracker is an example
- *
- * @var BOOL -- default false
- */
- var $disable_vardefs = false;
- /**
- * holds the full name of the user that an item is assigned to. Only used if notifications
- * are turned on and going to be sent out.
- *
- * @var String
- */
- var $new_assigned_user_name;
- /**
- * An array of booleans. This array is cleared out when data is loaded.
- * As date/times are converted, a "1" is placed under the key, the field is converted.
- *
- * @var Array of booleans
- */
- var $processed_dates_times = array();
- /**
- * Whether to process date/time fields for storage in the database in GMT
- *
- * @var BOOL
- */
- var $process_save_dates =true;
- /**
- * This signals to the bean that it is being saved in a mass mode.
- * Examples of this kind of save are import and mass update.
- * We turn off notificaitons of this is the case to make things more efficient.
- *
- * @var BOOL
- */
- var $save_from_post = true;
- /**
- * When running a query on related items using the method: retrieve_by_string_fields
- * this value will be set to true if more than one item matches the search criteria.
- *
- * @var BOOL
- */
- var $duplicates_found = false;
- /**
- * The DBManager instance that was used to load this bean and should be used for
- * future database interactions.
- *
- * @var DBManager
- */
- var $dbManager;
- /**
- * true if this bean has been deleted, false otherwise.
- *
- * @var BOOL
- */
- var $deleted = 0;
- /**
- * Should the date modified column of the bean be updated during save?
- * This is used for admin level functionality that should not be updating
- * the date modified. This is only used by sync to allow for updates to be
- * replicated in a way that will not cause them to be replicated back.
- *
- * @var BOOL
- */
- var $update_date_modified = true;
- /**
- * Should the modified by column of the bean be updated during save?
- * This is used for admin level functionality that should not be updating
- * the modified by column. This is only used by sync to allow for updates to be
- * replicated in a way that will not cause them to be replicated back.
- *
- * @var BOOL
- */
- var $update_modified_by = true;
- /**
- * Setting this to true allows for updates to overwrite the date_entered
- *
- * @var BOOL
- */
- var $update_date_entered = false;
- /**
- * This allows for seed data to be created without using the current uesr to set the id.
- * This should be replaced by altering the current user before the call to save.
- *
- * @var unknown_type
- */
- //TODO This should be replaced by altering the current user before the call to save.
- var $set_created_by = true;
- var $team_set_id;
- /**
- * The database table where records of this Bean are stored.
- *
- * @var String
- */
- var $table_name = '';
- /**
- * This is the singular name of the bean. (i.e. Contact).
- *
- * @var String
- */
- var $object_name = '';
- /** Set this to true if you query contains a sub-select and bean is converting both select statements
- * into count queries.
- */
- var $ungreedy_count=false;
- /**
- * The name of the module folder for this type of bean.
- *
- * @var String
- */
- var $module_dir = '';
- var $field_name_map;
- var $field_defs;
- var $custom_fields;
- var $column_fields = array();
- var $list_fields = array();
- var $additional_column_fields = array();
- var $relationship_fields = array();
- var $current_notify_user;
- var $fetched_row=false;
- var $layout_def;
- var $force_load_details = false;
- var $optimistic_lock = false;
- var $disable_custom_fields = false;
- var $number_formatting_done = false;
- var $process_field_encrypted=false;
- /*
- * The default ACL type
- */
- var $acltype = 'module';
- var $additional_meta_fields = array();
- /**
- * Set to true in the child beans if the module supports importing
- */
- var $importable = false;
- /**
- * Set to true in the child beans if the module use the special notification template
- */
- var $special_notification = false;
- /**
- * Set to true if the bean is being dealt with in a workflow
- */
- var $in_workflow = false;
- /**
- *
- * By default it will be true but if any module is to be kept non visible
- * to tracker, then its value needs to be overriden in that particular module to false.
- *
- */
- var $tracker_visibility = true;
- /**
- * Used to pass inner join string to ListView Data.
- */
- var $listview_inner_join = array();
- /**
- * Set to true in <modules>/Import/views/view.step4.php if a module is being imported
- */
- var $in_import = false;
- /**
- * Constructor for the bean, it performs following tasks:
- *
- * 1. Initalized a database connections
- * 2. Load the vardefs for the module implemeting the class. cache the entries
- * if needed
- * 3. Setup row-level security preference
- * All implementing classes must call this constructor using the parent::SugarBean() class.
- *
- */
- function SugarBean()
- {
- global $dictionary, $current_user;
- static $loaded_defs = array();
- $this->db = DBManagerFactory::getInstance();
- $this->dbManager = DBManagerFactory::getInstance();
- if((false == $this->disable_vardefs && empty($loaded_defs[$this->object_name])) || !empty($GLOBALS['reload_vardefs']))
- {
- VardefManager::loadVardef($this->module_dir, $this->object_name);
- // build $this->column_fields from the field_defs if they exist
- if (!empty($dictionary[$this->object_name]['fields'])) {
- foreach ($dictionary[$this->object_name]['fields'] as $key=>$value_array) {
- $column_fields[] = $key;
- if(!empty($value_array['required']) && !empty($value_array['name'])) {
- $this->required_fields[$value_array['name']] = 1;
- }
- }
- $this->column_fields = $column_fields;
- }
- //setup custom fields
- if(!isset($this->custom_fields) &&
- empty($this->disable_custom_fields))
- {
- $this->setupCustomFields($this->module_dir);
- }
- //load up field_arrays from CacheHandler;
- if(empty($this->list_fields))
- $this->list_fields = $this->_loadCachedArray($this->module_dir, $this->object_name, 'list_fields');
- if(empty($this->column_fields))
- $this->column_fields = $this->_loadCachedArray($this->module_dir, $this->object_name, 'column_fields');
- if(empty($this->required_fields))
- $this->required_fields = $this->_loadCachedArray($this->module_dir, $this->object_name, 'required_fields');
- if(isset($GLOBALS['dictionary'][$this->object_name]) && !$this->disable_vardefs)
- {
- $this->field_name_map = $dictionary[$this->object_name]['fields'];
- $this->field_defs = $dictionary[$this->object_name]['fields'];
- if(!empty($dictionary[$this->object_name]['optimistic_locking']))
- {
- $this->optimistic_lock=true;
- }
- }
- $loaded_defs[$this->object_name]['column_fields'] =& $this->column_fields;
- $loaded_defs[$this->object_name]['list_fields'] =& $this->list_fields;
- $loaded_defs[$this->object_name]['required_fields'] =& $this->required_fields;
- $loaded_defs[$this->object_name]['field_name_map'] =& $this->field_name_map;
- $loaded_defs[$this->object_name]['field_defs'] =& $this->field_defs;
- }
- else
- {
- $this->column_fields =& $loaded_defs[$this->object_name]['column_fields'] ;
- $this->list_fields =& $loaded_defs[$this->object_name]['list_fields'];
- $this->required_fields =& $loaded_defs[$this->object_name]['required_fields'];
- $this->field_name_map =& $loaded_defs[$this->object_name]['field_name_map'];
- $this->field_defs =& $loaded_defs[$this->object_name]['field_defs'];
- $this->added_custom_field_defs = true;
- if(!isset($this->custom_fields) &&
- empty($this->disable_custom_fields))
- {
- $this->setupCustomFields($this->module_dir, false);
- }
- if(!empty($dictionary[$this->object_name]['optimistic_locking']))
- {
- $this->optimistic_lock=true;
- }
- }
- if($this->bean_implements('ACL') && !empty($GLOBALS['current_user'])){
- $this->acl_fields = (isset($dictionary[$this->object_name]['acl_fields']) && $dictionary[$this->object_name]['acl_fields'] === false)?false:true;
- }
- $this->populateDefaultValues();
- }
- /**
- * Returns the object name. If object_name is not set, table_name is returned.
- *
- * All implementing classes must set a value for the object_name variable.
- *
- * @param array $arr row of data fetched from the database.
- * @return nothing
- *
- */
- function getObjectName()
- {
- if ($this->object_name)
- return $this->object_name;
- // This is a quick way out. The generated metadata files have the table name
- // as the key. The correct way to do this is to override this function
- // in bean and return the object name. That requires changing all the beans
- // as well as put the object name in the generator.
- return $this->table_name;
- }
- /**
- * Returns a list of fields with their definitions that have the audited property set to true.
- * Before calling this function, check whether audit has been enabled for the table/module or not.
- * You would set the audit flag in the implemting module's vardef file.
- *
- * @return an array of
- * @see is_AuditEnabled
- *
- * Internal function, do not override.
- */
- function getAuditEnabledFieldDefinitions()
- {
- $aclcheck = $this->bean_implements('ACL');
- $is_owner = $this->isOwner($GLOBALS['current_user']->id);
- if (!isset($this->audit_enabled_fields))
- {
- $this->audit_enabled_fields=array();
- foreach ($this->field_defs as $field => $properties)
- {
- if (
- (
- !empty($properties['Audited']) || !empty($properties['audited']))
- )
- {
- $this->audit_enabled_fields[$field]=$properties;
- }
- }
- }
- return $this->audit_enabled_fields;
- }
- /**
- * Return true if auditing is enabled for this object
- * You would set the audit flag in the implemting module's vardef file.
- *
- * @return boolean
- *
- * Internal function, do not override.
- */
- function is_AuditEnabled()
- {
- global $dictionary;
- if (isset($dictionary[$this->getObjectName()]['audited']))
- {
- return $dictionary[$this->getObjectName()]['audited'];
- }
- else
- {
- return false;
- }
- }
- /**
- * Returns the name of the audit table.
- * Audit table's name is based on implementing class' table name.
- *
- * @return String Audit table name.
- *
- * Internal function, do not override.
- */
- function get_audit_table_name()
- {
- return $this->getTableName().'_audit';
- }
- /**
- * If auditing is enabled, create the audit table.
- *
- * Function is used by the install scripts and a repair utility in the admin panel.
- *
- * Internal function, do not override.
- */
- function create_audit_table()
- {
- global $dictionary;
- $table_name=$this->get_audit_table_name();
- require('metadata/audit_templateMetaData.php');
- $fieldDefs = $dictionary['audit']['fields'];
- $indices = $dictionary['audit']['indices'];
- // '0' stands for the first index for all the audit tables
- $indices[0]['name'] = 'idx_' . strtolower($this->getTableName()) . '_' . $indices[0]['name'];
- $indices[1]['name'] = 'idx_' . strtolower($this->getTableName()) . '_' . $indices[1]['name'];
- $engine = null;
- if(isset($dictionary['audit']['engine'])) {
- $engine = $dictionary['audit']['engine'];
- } else if(isset($dictionary[$this->getObjectName()]['engine'])) {
- $engine = $dictionary[$this->getObjectName()]['engine'];
- }
- $sql=$this->dbManager->helper->createTableSQLParams($table_name, $fieldDefs, $indices, $engine);
- $msg = "Error creating table: ".$table_name. ":";
- $this->dbManager->query($sql,true,$msg);
- }
- /**
- * Returns the implementing class' table name.
- *
- * All implementing classes set a value for the table_name variable. This value is returned as the
- * table name. If not set, table name is extracted from the implementing module's vardef.
- *
- * @return String Table name.
- *
- * Internal function, do not override.
- */
- function getTableName()
- {
- global $dictionary;
- if(isset($this->table_name))
- {
- return $this->table_name;
- }
- return $dictionary[$this->getObjectName()]['table'];
- }
- /**
- * Returns field definitions for the implementing module.
- *
- * The definitions were loaded in the constructor.
- *
- * @return Array Field definitions.
- *
- * Internal function, do not override.
- */
- function getFieldDefinitions()
- {
- return $this->field_defs;
- }
- /**
- * Returns index definitions for the implementing module.
- *
- * The definitions were loaded in the constructor.
- *
- * @return Array Index definitions.
- *
- * Internal function, do not override.
- */
- function getIndices()
- {
- global $dictionary;
- if(isset($dictionary[$this->getObjectName()]['indices']))
- {
- return $dictionary[$this->getObjectName()]['indices'];
- }
- return array();
- }
- /**
- * Returns field definition for the requested field name.
- *
- * The definitions were loaded in the constructor.
- *
- * @param string field name,
- * @return Array Field properties or boolean false if the field doesn't exist
- *
- * Internal function, do not override.
- */
- function getFieldDefinition($name)
- {
- if ( !isset($this->field_defs[$name]) )
- return false;
- return $this->field_defs[$name];
- }
- /**
- * Returnss definition for the id field name.
- *
- * The definitions were loaded in the constructor.
- *
- * @return Array Field properties.
- *
- * Internal function, do not override.
- */
- function getPrimaryFieldDefinition()
- {
- $def = $this->getFieldDefinition("id");
- if (!$def)
- $def = $this->getFieldDefinition(0);
- return $def;
- }
- /**
- * Returns the value for the requested field.
- *
- * When a row of data is fetched using the bean, all fields are created as variables in the context
- * of the bean and then fetched values are set in these variables.
- *
- * @param string field name,
- * @return varies Field value.
- *
- * Internal function, do not override.
- */
- function getFieldValue($name)
- {
- if (!isset($this->$name)){
- return FALSE;
- }
- if($this->$name === TRUE){
- return 1;
- }
- if($this->$name === FALSE){
- return 0;
- }
- return $this->$name;
- }
- /**
- * Basically undoes the effects of SugarBean::populateDefaultValues(); this method is best called right after object
- * initialization.
- */
- public function unPopulateDefaultValues()
- {
- if ( !is_array($this->field_defs) )
- return;
- foreach ($this->field_defs as $field => $value) {
- if( !empty($this->$field)
- && ((isset($value['default']) && $this->$field == $value['default']) || (!empty($value['display_default']) && $this->$field == $value['display_default']))
- ) {
- $this->$field = null;
- continue;
- }
- if(!empty($this->$field) && !empty($value['display_default']) && in_array($value['type'], array('date', 'datetime', 'datetimecombo')) &&
- $this->$field == $this->parseDateDefault($value['display_default'], ($value['type'] != 'date'))) {
- $this->$field = null;
- }
- }
- }
- /**
- * Create date string from default value
- * like '+1 month'
- * @param string $value
- * @param bool $time Should be expect time set too?
- * @return string
- */
- protected function parseDateDefault($value, $time = false)
- {
- global $timedate;
- if($time) {
- $dtAry = explode('&', $value, 2);
- $dateValue = $timedate->getNow(true)->modify($dtAry[0]);
- if(!empty($dtAry[1])) {
- $timeValue = $timedate->fromString($dtAry[1]);
- $dateValue->setTime($timeValue->hour, $timeValue->min, $timeValue->sec);
- }
- return $timedate->asUser($dateValue);
- } else {
- return $timedate->asUserDate($timedate->getNow(true)->modify($value));
- }
- }
- function populateDefaultValues($force=false){
- if ( !is_array($this->field_defs) )
- return;
- foreach($this->field_defs as $field=>$value){
- if((isset($value['default']) || !empty($value['display_default'])) && ($force || empty($this->$field))){
- $type = $value['type'];
- switch($type){
- case 'date':
- if(!empty($value['display_default'])){
- $this->$field = $this->parseDateDefault($value['display_default']);
- }
- break;
- case 'datetime':
- case 'datetimecombo':
- if(!empty($value['display_default'])){
- $this->$field = $this->parseDateDefault($value['display_default'], true);
- }
- break;
- case 'multienum':
- if(empty($value['default']) && !empty($value['display_default']))
- $this->$field = $value['display_default'];
- else
- $this->$field = $value['default'];
- break;
- default:
- if ( isset($value['default']) && $value['default'] !== '' ) {
- $this->$field = htmlentities($value['default'], ENT_QUOTES, 'UTF-8');
- } else {
- $this->$field = '';
- }
- } //switch
- }
- } //foreach
- }
- /**
- * Removes relationship metadata cache.
- *
- * Every module that has relationships defined with other modules, has this meta data cached. The cache is
- * stores in 2 locations: relationships table and file system. This method clears the cache from both locations.
- *
- * @param string $key module whose meta cache is to be cleared.
- * @param string $db database handle.
- * @param string $tablename table name
- * @param string $dictionary vardef for the module
- * @param string $module_dir name of subdirectory where module is installed.
- *
- * @return Nothing
- * @static
- *
- * Internal function, do not override.
- */
- function removeRelationshipMeta($key,$db,$tablename,$dictionary,$module_dir)
- {
- //load the module dictionary if not supplied.
- if ((!isset($dictionary) or empty($dictionary)) && !empty($module_dir))
- {
- $filename='modules/'. $module_dir . '/vardefs.php';
- if(file_exists($filename))
- {
- include($filename);
- }
- }
- if (!is_array($dictionary) or !array_key_exists($key, $dictionary))
- {
- $GLOBALS['log']->fatal("removeRelationshipMeta: Metadata for table ".$tablename. " does not exist");
- display_notice("meta data absent for table ".$tablename." keyed to $key ");
- }
- else
- {
- if (isset($dictionary[$key]['relationships']))
- {
- $RelationshipDefs = $dictionary[$key]['relationships'];
- foreach ($RelationshipDefs as $rel_name)
- {
- Relationship::delete($rel_name,$db);
- }
- }
- }
- }
- /**
- * This method has been deprecated.
- *
- * @see removeRelationshipMeta()
- * @deprecated 4.5.1 - Nov 14, 2006
- * @static
- */
- function remove_relationship_meta($key,$db,$log,$tablename,$dictionary,$module_dir)
- {
- SugarBean::removeRelationshipMeta($key,$db,$tablename,$dictionary,$module_dir);
- }
- /**
- * Populates the relationship meta for a module.
- *
- * It is called during setup/install. It is used statically to create relationship meta data for many-to-many tables.
- *
- * @param string $key name of the object.
- * @param object $db database handle.
- * @param string $tablename table, meta data is being populated for.
- * @param array dictionary vardef dictionary for the object. *
- * @param string module_dir name of subdirectory where module is installed.
- * @param boolean $iscustom Optional,set to true if module is installed in a custom directory. Default value is false.
- * @static
- *
- * Internal function, do not override.
- */
- function createRelationshipMeta($key,$db,$tablename,$dictionary,$module_dir,$iscustom=false)
- {
- //load the module dictionary if not supplied.
- if (empty($dictionary) && !empty($module_dir))
- {
- if($iscustom)
- {
- $filename='custom/modules/' . $module_dir . '/Ext/Vardefs/vardefs.ext.php';
- }
- else
- {
- if ($key == 'User')
- {
- // a very special case for the Employees module
- // this must be done because the Employees/vardefs.php does an include_once on
- // Users/vardefs.php
- $filename='modules/Users/vardefs.php';
- }
- else
- {
- $filename='modules/'. $module_dir . '/vardefs.php';
- }
- }
- if(file_exists($filename))
- {
- include($filename);
- // cn: bug 7679 - dictionary entries defined as $GLOBALS['name'] not found
- if(empty($dictionary) || !empty($GLOBALS['dictionary'][$key]))
- {
- $dictionary = $GLOBALS['dictionary'];
- }
- }
- else
- {
- $GLOBALS['log']->debug("createRelationshipMeta: no metadata file found" . $filename);
- return;
- }
- }
- if (!is_array($dictionary) or !array_key_exists($key, $dictionary))
- {
- $GLOBALS['log']->fatal("createRelationshipMeta: Metadata for table ".$tablename. " does not exist");
- display_notice("meta data absent for table ".$tablename." keyed to $key ");
- }
- else
- {
- if (isset($dictionary[$key]['relationships']))
- {
- $RelationshipDefs = $dictionary[$key]['relationships'];
- $delimiter=',';
- global $beanList;
- $beanList_ucase=array_change_key_case ( $beanList ,CASE_UPPER);
- foreach ($RelationshipDefs as $rel_name=>$rel_def)
- {
- if (isset($rel_def['lhs_module']) and !isset($beanList_ucase[strtoupper($rel_def['lhs_module'])])) {
- $GLOBALS['log']->debug('skipping orphaned relationship record ' . $rel_name . ' lhs module is missing ' . $rel_def['lhs_module']);
- continue;
- }
- if (isset($rel_def['rhs_module']) and !isset($beanList_ucase[strtoupper($rel_def['rhs_module'])])) {
- $GLOBALS['log']->debug('skipping orphaned relationship record ' . $rel_name . ' rhs module is missing ' . $rel_def['rhs_module']);
- continue;
- }
- //check whether relationship exists or not first.
- if (Relationship::exists($rel_name,$db))
- {
- $GLOBALS['log']->debug('Skipping, reltionship already exists '.$rel_name);
- }
- else
- {
- // add Id to the insert statement.
- $column_list='id';
- $value_list="'".create_guid()."'";
- //add relationship name to the insert statement.
- $column_list .= $delimiter.'relationship_name';
- $value_list .= $delimiter."'".$rel_name."'";
- //todo check whether $rel_def is an array or not.
- //for now make that assumption.
- //todo specify defaults if meta not defined.
- foreach ($rel_def as $def_key=>$value)
- {
- $column_list.= $delimiter.$def_key;
- $value_list.= $delimiter."'".$value."'";
- }
- //create the record. todo add error check.
- $insert_string = "INSERT into relationships (" .$column_list. ") values (".$value_list.")";
- $db->query($insert_string, true);
- }
- }
- }
- else
- {
- //todo
- //log informational message stating no relationships meta was set for this bean.
- }
- }
- }
- /**
- * This method has been deprecated.
- * @see createRelationshipMeta()
- * @deprecated 4.5.1 - Nov 14, 2006
- * @static
- */
- function create_relationship_meta($key,&$db,&$log,$tablename,$dictionary,$module_dir)
- {
- SugarBean::createRelationshipMeta($key,$db,$tablename,$dictionary,$module_dir);
- }
- /**
- * Loads the request relationship. This method should be called before performing any operations on the related data.
- *
- * This method searches the vardef array for the requested attribute's definition. If the attribute is of the type
- * link then it creates a similary named variable and loads the relationship definition.
- *
- * @param string $rel_name relationship/attribute name.
- * @return nothing.
- */
- function load_relationship($rel_name)
- {
- $GLOBALS['log']->debug("SugarBean.load_relationships, Loading relationship (".$rel_name.").");
- if (empty($rel_name))
- {
- $GLOBALS['log']->error("SugarBean.load_relationships, Null relationship name passed.");
- return false;
- }
- $fieldDefs = $this->getFieldDefinitions();
- //find all definitions of type link.
- if (!empty($fieldDefs))
- {
- //if rel_name is provided, search the fieldef array keys by name.
- if (array_key_exists($rel_name, $fieldDefs))
- {
- if (array_search('link',$fieldDefs[$rel_name]) === 'type')
- {
- //initialize a variable of type Link
- require_once('data/Link.php');
- $class = load_link_class($fieldDefs[$rel_name]);
- $this->$rel_name=new $class($fieldDefs[$rel_name]['relationship'], $this, $fieldDefs[$rel_name]);
- if (empty($this->$rel_name->_relationship->id)) {
- unset($this->$rel_name);
- return false;
- }
- return true;
- }
- }
- else
- {
- $GLOBALS['log']->debug("SugarBean.load_relationships, Error Loading relationship (".$rel_name.").");
- return false;
- }
- }
- return false;
- }
- /**
- * Loads all attributes of type link.
- *
- * Method searches the implmenting module's vardef file for attributes of type link, and for each attribute
- * create a similary named variable and load the relationship definition.
- *
- * @return Nothing
- *
- * Internal function, do not override.
- */
- function load_relationships()
- {
- $GLOBALS['log']->debug("SugarBean.load_relationships, Loading all relationships of type link.");
- $linked_fields=$this->get_linked_fields();
- require_once("data/Link.php");
- foreach($linked_fields as $name=>$properties)
- {
- $class = load_link_class($properties);
- $this->$name=new $class($properties['relationship'], $this, $properties);
- }
- }
- /**
- * Returns an array of beans of related data.
- *
- * For instance, if an account is related to 10 contacts , this function will return an array of contacts beans (10)
- * with each bean representing a contact record.
- * Method will load the relationship if not done so already.
- *
- * @param string $field_name relationship to be loaded.
- * @param string $bean name class name of the related bean.
- * @param array $sort_array optional, unused
- * @param int $begin_index Optional, default 0, unused.
- * @param int $end_index Optional, default -1
- * @param int $deleted Optional, Default 0, 0 adds deleted=0 filter, 1 adds deleted=1 filter.
- * @param string $optional_where, Optional, default empty.
- *
- * Internal function, do not override.
- */
- function get_linked_beans($field_name,$bean_name, $sort_array = array(), $begin_index = 0, $end_index = -1,
- $deleted=0, $optional_where="")
- {
- //if bean_name is Case then use aCase
- if($bean_name=="Case")
- $bean_name = "aCase";
- //add a references to bean_name if it doe not exist aleady.
- if (!(class_exists($bean_name)))
- {
- if (isset($GLOBALS['beanList']) && isset($GLOBALS['beanFiles']))
- {
- global $beanFiles;
- }
- else
- {
- }
- $bean_file=$beanFiles[$bean_name];
- include_once($bean_file);
- }
- $this->load_relationship($field_name);
- return $this->$field_name->getBeans(new $bean_name(), $sort_array, $begin_index, $end_index, $deleted, $optional_where);
- }
- /**
- * Returns an array of fields that are of type link.
- *
- * @return array List of fields.
- *
- * Internal function, do not override.
- */
- function get_linked_fields()
- {
- $linked_fields=array();
- // require_once('data/Link.php');
- $fieldDefs = $this->getFieldDefinitions();
- //find all definitions of type link.
- if (!empty($fieldDefs))
- {
- foreach ($fieldDefs as $name=>$properties)
- {
- if (array_search('link',$properties) === 'type')
- {
- $linked_fields[$name]=$properties;
- }
- }
- }
- return $linked_fields;
- }
- /**
- * Returns an array of fields that are able to be Imported into
- * i.e. 'importable' not set to 'false'
- *
- * @return array List of fields.
- *
- * Internal function, do not override.
- */
- function get_importable_fields()
- {
- $importableFields = array();
- $fieldDefs= $this->getFieldDefinitions();
- if (!empty($fieldDefs)) {
- foreach ($fieldDefs as $key=>$value_array) {
- if ( (isset($value_array['importable'])
- && (is_string($value_array['importable']) && $value_array['importable'] == 'false'
- || is_bool($value_array['importable']) && $value_array['importable'] == false))
- || (isset($value_array['type']) && $value_array['type'] == 'link')
- || (isset($value_array['auto_increment'])
- && ($value_array['type'] == true || $value_array['type'] == 'true')) ) {
- // only allow import if we force it
- if (isset($value_array['importable'])
- && (is_string($value_array['importable']) && $value_array['importable'] == 'true'
- || is_bool($value_array['importable']) && $value_array['importable'] == true)) {
- $importableFields[$key]=$value_array;
- }
- }
- else {
- $importableFields[$key]=$value_array;
- }
- }
- }
- return $importableFields;
- }
- /**
- * Returns an array of fields that are of type relate.
- *
- * @return array List of fields.
- *
- * Internal function, do not override.
- */
- function get_related_fields()
- {
- $related_fields=array();
- // require_once('data/Link.php');
- $fieldDefs = $this->getFieldDefinitions();
- //find all definitions of type link.
- if (!empty($fieldDefs))
- {
- foreach ($fieldDefs as $name=>$properties)
- {
- if (array_search('relate',$properties) === 'type')
- {
- $related_fields[$name]=$properties;
- }
- }
- }
- return $related_fields;
- }
- /**
- * Returns an array of fields that are required for import
- *
- * @return array
- */
- function get_import_required_fields()
- {
- $importable_fields = $this->get_importable_fields();
- $required_fields = array();
- foreach ( $importable_fields as $name => $properties ) {
- if ( isset($properties['importable']) && is_string($properties['importable']) && $properties['importable'] == 'required' ) {
- $required_fields[$name] = $properties;
- }
- }
- return $required_fields;
- }
- /**
- * Iterates through all the relationships and deletes all records for reach relationship.
- *
- * @param string $id Primary key value of the parent reocrd
- */
- function delete_linked($id)
- {
- $linked_fields=$this->get_linked_fields();
- foreach ($linked_fields as $name => $value)
- {
- if ($this->load_relationship($name))
- {
- $GLOBALS['log']->debug('relationship loaded');
- $this->$name->delete($id);
- }
- else
- {
- $GLOBALS['log']->error('error loading relationship');
- }
- }
- }
- /**
- * Creates tables for the module implementing the class.
- * If you override this function make sure that your code can handles table creation.
- *
- */
- function create_tables()
- {
- global $dictionary;
- $key = $this->getObjectName();
- if (!array_key_exists($key, $dictionary))
- {
- $GLOBALS['log']->fatal("create_tables: Metadata for table ".$this->table_name. " does not exist");
- display_notice("meta data absent for table ".$this->table_name." keyed to $key ");
- }
- else
- {
- if(!$this->db->tableExists($this->table_name))
- {
- $this->dbManager->createTable($this);
- if($this->bean_implements('ACL')){
- if(!empty($this->acltype)){
- ACLAction::addActions($this->getACLCategory(), $this->acltype);
- }else{
- ACLAction::addActions($this->getACLCategory());
- }
- }
- }
- else
- {
- echo "Table already exists : $this->table_name<br>";
- }
- if($this->is_AuditEnabled()){
- if (!$this->db->tableExists($this->get_audit_table_name())) {
- $this->create_audit_table();
- }
- }
- }
- }
- /**
- * Delete the primary table for the module implementing the class.
- * If custom fields were added to this table/module, the custom table will be removed too, along with the cache
- * entries that define the custom fields.
- *
- */
- function drop_tables()
- {
- global $dictionary;
- $key = $this->getObjectName();
- if (!array_key_exists($key, $dictionary))
- {
- $GLOBALS['log']->fatal("drop_tables: Metadata for table ".$this->table_name. " does not exist");
- echo "meta data absent for table ".$this->table_name."<br>\n";
- } else {
- if(empty($this->table_name))return;
- if ($this->db->tableExists($this->table_name))
- $this->dbManager->dropTable($this);
- if ($this->db->tableExists($this->table_name. '_cstm'))
- {
- $this->dbManager->dropTableName($this->table_name. '_cstm');
- DynamicField::deleteCache();
- }
- if ($this->db->tableExists($this->get_audit_table_name())) {
- $this->dbManager->dropTableName($this->get_audit_table_name());
- }
- }
- }
- /**
- * Loads the definition of custom fields defined for the module.
- * Local file system cache is created as needed.
- *
- * @param string $module_name setting up custom fields for this module.
- * @param boolean $clean_load Optional, default true, rebuilds the cache if set to true.
- */
- function setupCustomFields($module_name, $clean_load=true)
- {
- $this->custom_fields = new DynamicField($module_name);
- $this->custom_fields->setup($this);
- }
- /**
- * Cleans char, varchar, text, etc. fields of XSS type materials
- */
- function cleanBean() {
- foreach($this->field_defs as $key => $def) {
- if (isset($def['type'])) {
- $type=$def['type'];
- }
- if(isset($def['dbType']))
- $type .= $def['dbType'];
- if((strpos($type, 'char') !== false ||
- strpos($type, 'text') !== false ||
- $type == 'enum') &&
- !empty($this->$key)
- ) {
- $str = from_html($this->$key);
- // Julian's XSS cleaner
- $potentials = clean_xss($str, false);
- if(is_array($potentials) && !empty($potentials)) {
- foreach($potentials as $bad) {
- $str = str_replace($bad, "", $str);
- }
- $this->$key = to_html($str);
- }
- }
- }
- }
- /**
- * Implements a generic insert and update logic for any SugarBean
- * This method only works for subclasses that implement the same variable names.
- * This method uses the presence of an id field that is not null to signify and update.
- * The id field should not be set otherwise.
- *
- * @param boolean $check_notify Optional, default false, if set to true assignee of the record is notified via email.
- * @todo Add support for field type validation and encoding of parameters.
- */
- function save($check_notify = FALSE)
- {
- // cn: SECURITY - strip XSS potential vectors
- $this->cleanBean();
- // This is used so custom/3rd-party code can be upgraded with fewer issues, this will be removed in a future release
- $this->fixUpFormatting();
- global $timedate;
- global $current_user, $action;
- $isUpdate = true;
- if(empty($this->id))
- {
- $isUpdate = false;
- }
- if ( $this->new_with_id == true )
- {
- $isUpdate = false;
- }
- if(empty($this->date_modified) || $this->update_date_modified)
- {
- $this->date_modified = $GLOBALS['timedate']->nowDb();
- }
- $this->_checkOptimisticLocking($action, $isUpdate);
- if(!empty($this->modified_by_name)) $this->old_modified_by_name = $this->modified_by_name;
- if($this->update_modified_by)
- {
- $this->modified_user_id = 1;
- if (!empty($current_user))
- {
- $this->modified_user_id = $current_user->id;
- $this->modified_by_name = $current_user->user_name;
- }
- }
- if ($this->deleted != 1)
- $this->deleted = 0;
- if($isUpdate)
- {
- $query = "Update ";
- }
- else
- {
- if (empty($this->date_entered))
- {
- $this->date_entered = $this->date_modified;
- }
- if($this->set_created_by == true)
- {
- // created by should always be this user
- $this->created_by = (isset($current_user)) ? $current_user->id : "";
- }
- if( $this->new_with_id == false)
- {
- $this->id = create_guid();
- }
- $query = "INSERT into ";
- }
- if($isUpdate && !$this->update_date_entered)
- {
- unset($this->date_entered);
- }
- // call the custom business logic
- $custom_logic_arguments['check_notify'] = $check_notify;
- $this->call_custom_logic("before_save", $custom_logic_arguments);
- unset($custom_logic_arguments);
- if(isset($this->custom_fields))
- {
- $this->custom_fields->bean = $this;
- $this->custom_fields->save($isUpdate);
- }
- // use the db independent query generator
- $this->preprocess_fields_on_save();
- //construct the SQL to create the audit record if auditing is enabled.
- $dataChanges=array();
- if ($this->is_AuditEnabled())
- {
- if ($isUpdate && !isset($this->fetched_row))
- {
- $GLOBALS['log']->debug('Auditing: Retrieve was not called, audit record will not be created.');
- }
- else
- {
- $dataChanges=$this->dbManager->helper->getDataChanges($this);
- }
- }
- $this->_sendNotifications($check_notify);
- if ($this->db->dbType == "oci8")
- {
- }
- if ($this->db->dbType == 'mysql')
- {
- // write out the SQL statement.
- $query .= $this->table_name." set ";
- $firstPass = 0;
- foreach($this->field_defs as $field=>$value)
- {
- if(!isset($value['source']) || $value['source'] == 'db')
- {
- // Do not write out the id field on the update statement.
- // We are not allowed to change ids.
- if($isUpdate && ('id' == $field))
- continue;
- //custom fields handle there save seperatley
- if(isset($this->field_name_map) && !empty($this->field_name_map[$field]['custom_type']))
- continue;
- // Only assign variables that have been set.
- if(isset($this->$field))
- {
- //bug: 37908 - this is to handle the issue where the bool value is false, but strlen(false) <= so it will
- //set the default value. TODO change this code to esend all fields through getFieldValue() like DbHelper->insertSql
- if(!empty($value['type']) && $value['type'] == 'bool'){
- $this->$field = $this->getFieldValue($field);
- }
- if(strlen($this->$field) <= 0)
- {
- if(!$isUpdate && isset($value['default']) && (strlen($value['default']) > 0))
- {
- $this->$field = $value['default'];
- }
- else
- {
- $this->$field = null;
- }
- }
- // Try comparing this element with the head element.
- if(0 == $firstPass)
- $firstPass = 1;
- else
- $query .= ", ";
- if(is_null($this->$field))
- {
- $query .= $field."=null";
- }
- else
- {
- //added check for ints because sql-server does not like casting varchar with a decimal value
- //into an int.
- if(isset($value['type']) and $value['type']=='int') {
- $query .= $field."=".$this->db->quote($this->$field);
- } elseif ( isset($value['len']) ) {
- $query .= $field."='".$this->db->quote($this->db->truncate(from_html($this->$field),$value['len']))."'";
- } else {
- $query .= $field."='".$this->db->quote($this->$field)."'";
- }
- }
- }
- }
- }
- if($isUpdate)
- {
- $qu…
Large files files are truncated, but you can click here to view the full file