PageRenderTime 71ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 2ms

/SugarCRM/data/SugarBean.php

https://github.com/guolong/ggxw
PHP | 5551 lines | 3754 code | 516 blank | 1281 comment | 1001 complexity | 592c181708651b1b9faed97f9f41df68 MD5 | raw file
Possible License(s): AGPL-3.0, LGPL-2.1, MPL-2.0-no-copyleft-exception

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
  3. /*********************************************************************************
  4. * SugarCRM Community Edition is a customer relationship management program developed by
  5. * SugarCRM, Inc. Copyright (C) 2004-2012 SugarCRM Inc.
  6. *
  7. * This program is free software; you can redistribute it and/or modify it under
  8. * the terms of the GNU Affero General Public License version 3 as published by the
  9. * Free Software Foundation with the addition of the following permission added
  10. * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
  11. * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
  12. * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
  13. *
  14. * This program is distributed in the hope that it will be useful, but WITHOUT
  15. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  16. * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
  17. * details.
  18. *
  19. * You should have received a copy of the GNU Affero General Public License along with
  20. * this program; if not, see http://www.gnu.org/licenses or write to the Free
  21. * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  22. * 02110-1301 USA.
  23. *
  24. * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
  25. * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
  26. *
  27. * The interactive user interfaces in modified source and object code versions
  28. * of this program must display Appropriate Legal Notices, as required under
  29. * Section 5 of the GNU Affero General Public License version 3.
  30. *
  31. * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
  32. * these Appropriate Legal Notices must retain the display of the "Powered by
  33. * SugarCRM" logo. If the display of the logo is not reasonably feasible for
  34. * technical reasons, the Appropriate Legal Notices must display the words
  35. * "Powered by SugarCRM".
  36. ********************************************************************************/
  37. /*********************************************************************************
  38. * Description: Defines the base class for all data entities used throughout the
  39. * application. The base class including its methods and variables is designed to
  40. * be overloaded with module-specific methods and variables particular to the
  41. * module's base entity class.
  42. * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc.
  43. * All Rights Reserved.
  44. *******************************************************************************/
  45. require_once('modules/DynamicFields/DynamicField.php');
  46. require_once("data/Relationships/RelationshipFactory.php");
  47. /**
  48. * SugarBean is the base class for all business objects in Sugar. It implements
  49. * the primary functionality needed for manipulating business objects: create,
  50. * retrieve, update, delete. It allows for searching and retrieving list of records.
  51. * It allows for retrieving related objects (e.g. contacts related to a specific account).
  52. *
  53. * In the current implementation, there can only be one bean per folder.
  54. * Naming convention has the bean name be the same as the module and folder name.
  55. * All bean names should be singular (e.g. Contact). The primary table name for
  56. * a bean should be plural (e.g. contacts).
  57. * @api
  58. */
  59. class SugarBean
  60. {
  61. /**
  62. * A pointer to the database object
  63. *
  64. * @var DBManager
  65. */
  66. var $db;
  67. /**
  68. * When createing a bean, you can specify a value in the id column as
  69. * long as that value is unique. During save, if the system finds an
  70. * id, it assumes it is an update. Setting new_with_id to true will
  71. * make sure the system performs an insert instead of an update.
  72. *
  73. * @var BOOL -- default false
  74. */
  75. var $new_with_id = false;
  76. /**
  77. * How deep logic hooks can go
  78. * @var int
  79. */
  80. protected $max_logic_depth = 10;
  81. /**
  82. * Disble vardefs. This should be set to true only for beans that do not have varders. Tracker is an example
  83. *
  84. * @var BOOL -- default false
  85. */
  86. var $disable_vardefs = false;
  87. /**
  88. * holds the full name of the user that an item is assigned to. Only used if notifications
  89. * are turned on and going to be sent out.
  90. *
  91. * @var String
  92. */
  93. var $new_assigned_user_name;
  94. /**
  95. * An array of booleans. This array is cleared out when data is loaded.
  96. * As date/times are converted, a "1" is placed under the key, the field is converted.
  97. *
  98. * @var Array of booleans
  99. */
  100. var $processed_dates_times = array();
  101. /**
  102. * Whether to process date/time fields for storage in the database in GMT
  103. *
  104. * @var BOOL
  105. */
  106. var $process_save_dates =true;
  107. /**
  108. * This signals to the bean that it is being saved in a mass mode.
  109. * Examples of this kind of save are import and mass update.
  110. * We turn off notificaitons of this is the case to make things more efficient.
  111. *
  112. * @var BOOL
  113. */
  114. var $save_from_post = true;
  115. /**
  116. * When running a query on related items using the method: retrieve_by_string_fields
  117. * this value will be set to true if more than one item matches the search criteria.
  118. *
  119. * @var BOOL
  120. */
  121. var $duplicates_found = false;
  122. /**
  123. * true if this bean has been deleted, false otherwise.
  124. *
  125. * @var BOOL
  126. */
  127. var $deleted = 0;
  128. /**
  129. * Should the date modified column of the bean be updated during save?
  130. * This is used for admin level functionality that should not be updating
  131. * the date modified. This is only used by sync to allow for updates to be
  132. * replicated in a way that will not cause them to be replicated back.
  133. *
  134. * @var BOOL
  135. */
  136. var $update_date_modified = true;
  137. /**
  138. * Should the modified by column of the bean be updated during save?
  139. * This is used for admin level functionality that should not be updating
  140. * the modified by column. This is only used by sync to allow for updates to be
  141. * replicated in a way that will not cause them to be replicated back.
  142. *
  143. * @var BOOL
  144. */
  145. var $update_modified_by = true;
  146. /**
  147. * Setting this to true allows for updates to overwrite the date_entered
  148. *
  149. * @var BOOL
  150. */
  151. var $update_date_entered = false;
  152. /**
  153. * This allows for seed data to be created without using the current uesr to set the id.
  154. * This should be replaced by altering the current user before the call to save.
  155. *
  156. * @var unknown_type
  157. */
  158. //TODO This should be replaced by altering the current user before the call to save.
  159. var $set_created_by = true;
  160. var $team_set_id;
  161. /**
  162. * The database table where records of this Bean are stored.
  163. *
  164. * @var String
  165. */
  166. var $table_name = '';
  167. /**
  168. * This is the singular name of the bean. (i.e. Contact).
  169. *
  170. * @var String
  171. */
  172. var $object_name = '';
  173. /** Set this to true if you query contains a sub-select and bean is converting both select statements
  174. * into count queries.
  175. */
  176. var $ungreedy_count=false;
  177. /**
  178. * The name of the module folder for this type of bean.
  179. *
  180. * @var String
  181. */
  182. var $module_dir = '';
  183. var $module_name = '';
  184. var $field_name_map;
  185. var $field_defs;
  186. var $custom_fields;
  187. var $column_fields = array();
  188. var $list_fields = array();
  189. var $additional_column_fields = array();
  190. var $relationship_fields = array();
  191. var $current_notify_user;
  192. var $fetched_row=false;
  193. var $fetched_rel_row = array();
  194. var $layout_def;
  195. var $force_load_details = false;
  196. var $optimistic_lock = false;
  197. var $disable_custom_fields = false;
  198. var $number_formatting_done = false;
  199. var $process_field_encrypted=false;
  200. /*
  201. * The default ACL type
  202. */
  203. var $acltype = 'module';
  204. var $additional_meta_fields = array();
  205. /**
  206. * Set to true in the child beans if the module supports importing
  207. */
  208. var $importable = false;
  209. /**
  210. * Set to true in the child beans if the module use the special notification template
  211. */
  212. var $special_notification = false;
  213. /**
  214. * Set to true if the bean is being dealt with in a workflow
  215. */
  216. var $in_workflow = false;
  217. /**
  218. *
  219. * By default it will be true but if any module is to be kept non visible
  220. * to tracker, then its value needs to be overriden in that particular module to false.
  221. *
  222. */
  223. var $tracker_visibility = true;
  224. /**
  225. * Used to pass inner join string to ListView Data.
  226. */
  227. var $listview_inner_join = array();
  228. /**
  229. * Set to true in <modules>/Import/views/view.step4.php if a module is being imported
  230. */
  231. var $in_import = false;
  232. /**
  233. * A way to keep track of the loaded relationships so when we clone the object we can unset them.
  234. *
  235. * @var array
  236. */
  237. protected $loaded_relationships = array();
  238. /**
  239. * set to true if dependent fields updated
  240. */
  241. protected $is_updated_dependent_fields = false;
  242. /**
  243. * Constructor for the bean, it performs following tasks:
  244. *
  245. * 1. Initalized a database connections
  246. * 2. Load the vardefs for the module implemeting the class. cache the entries
  247. * if needed
  248. * 3. Setup row-level security preference
  249. * All implementing classes must call this constructor using the parent::SugarBean() class.
  250. *
  251. */
  252. function SugarBean()
  253. {
  254. global $dictionary, $current_user;
  255. static $loaded_defs = array();
  256. $this->db = DBManagerFactory::getInstance();
  257. if (empty($this->module_name))
  258. $this->module_name = $this->module_dir;
  259. if((false == $this->disable_vardefs && empty($loaded_defs[$this->object_name])) || !empty($GLOBALS['reload_vardefs']))
  260. {
  261. VardefManager::loadVardef($this->module_dir, $this->object_name);
  262. // build $this->column_fields from the field_defs if they exist
  263. if (!empty($dictionary[$this->object_name]['fields'])) {
  264. foreach ($dictionary[$this->object_name]['fields'] as $key=>$value_array) {
  265. $column_fields[] = $key;
  266. if(!empty($value_array['required']) && !empty($value_array['name'])) {
  267. $this->required_fields[$value_array['name']] = 1;
  268. }
  269. }
  270. $this->column_fields = $column_fields;
  271. }
  272. //setup custom fields
  273. if(!isset($this->custom_fields) &&
  274. empty($this->disable_custom_fields))
  275. {
  276. $this->setupCustomFields($this->module_dir);
  277. }
  278. //load up field_arrays from CacheHandler;
  279. if(empty($this->list_fields))
  280. $this->list_fields = $this->_loadCachedArray($this->module_dir, $this->object_name, 'list_fields');
  281. if(empty($this->column_fields))
  282. $this->column_fields = $this->_loadCachedArray($this->module_dir, $this->object_name, 'column_fields');
  283. if(empty($this->required_fields))
  284. $this->required_fields = $this->_loadCachedArray($this->module_dir, $this->object_name, 'required_fields');
  285. if(isset($GLOBALS['dictionary'][$this->object_name]) && !$this->disable_vardefs)
  286. {
  287. $this->field_name_map = $dictionary[$this->object_name]['fields'];
  288. $this->field_defs = $dictionary[$this->object_name]['fields'];
  289. if(!empty($dictionary[$this->object_name]['optimistic_locking']))
  290. {
  291. $this->optimistic_lock=true;
  292. }
  293. }
  294. $loaded_defs[$this->object_name]['column_fields'] =& $this->column_fields;
  295. $loaded_defs[$this->object_name]['list_fields'] =& $this->list_fields;
  296. $loaded_defs[$this->object_name]['required_fields'] =& $this->required_fields;
  297. $loaded_defs[$this->object_name]['field_name_map'] =& $this->field_name_map;
  298. $loaded_defs[$this->object_name]['field_defs'] =& $this->field_defs;
  299. }
  300. else
  301. {
  302. $this->column_fields =& $loaded_defs[$this->object_name]['column_fields'] ;
  303. $this->list_fields =& $loaded_defs[$this->object_name]['list_fields'];
  304. $this->required_fields =& $loaded_defs[$this->object_name]['required_fields'];
  305. $this->field_name_map =& $loaded_defs[$this->object_name]['field_name_map'];
  306. $this->field_defs =& $loaded_defs[$this->object_name]['field_defs'];
  307. $this->added_custom_field_defs = true;
  308. if(!isset($this->custom_fields) &&
  309. empty($this->disable_custom_fields))
  310. {
  311. $this->setupCustomFields($this->module_dir, false);
  312. }
  313. if(!empty($dictionary[$this->object_name]['optimistic_locking']))
  314. {
  315. $this->optimistic_lock=true;
  316. }
  317. }
  318. if($this->bean_implements('ACL') && !empty($GLOBALS['current_user'])){
  319. $this->acl_fields = (isset($dictionary[$this->object_name]['acl_fields']) && $dictionary[$this->object_name]['acl_fields'] === false)?false:true;
  320. }
  321. $this->populateDefaultValues();
  322. }
  323. /**
  324. * Returns the object name. If object_name is not set, table_name is returned.
  325. *
  326. * All implementing classes must set a value for the object_name variable.
  327. *
  328. * @param array $arr row of data fetched from the database.
  329. * @return nothing
  330. *
  331. */
  332. function getObjectName()
  333. {
  334. if ($this->object_name)
  335. return $this->object_name;
  336. // This is a quick way out. The generated metadata files have the table name
  337. // as the key. The correct way to do this is to override this function
  338. // in bean and return the object name. That requires changing all the beans
  339. // as well as put the object name in the generator.
  340. return $this->table_name;
  341. }
  342. /**
  343. * Returns a list of fields with their definitions that have the audited property set to true.
  344. * Before calling this function, check whether audit has been enabled for the table/module or not.
  345. * You would set the audit flag in the implemting module's vardef file.
  346. *
  347. * @return an array of
  348. * @see is_AuditEnabled
  349. *
  350. * Internal function, do not override.
  351. */
  352. function getAuditEnabledFieldDefinitions()
  353. {
  354. $aclcheck = $this->bean_implements('ACL');
  355. $is_owner = $this->isOwner($GLOBALS['current_user']->id);
  356. if (!isset($this->audit_enabled_fields))
  357. {
  358. $this->audit_enabled_fields=array();
  359. foreach ($this->field_defs as $field => $properties)
  360. {
  361. if (
  362. (
  363. !empty($properties['Audited']) || !empty($properties['audited']))
  364. )
  365. {
  366. $this->audit_enabled_fields[$field]=$properties;
  367. }
  368. }
  369. }
  370. return $this->audit_enabled_fields;
  371. }
  372. /**
  373. * Return true if auditing is enabled for this object
  374. * You would set the audit flag in the implemting module's vardef file.
  375. *
  376. * @return boolean
  377. *
  378. * Internal function, do not override.
  379. */
  380. function is_AuditEnabled()
  381. {
  382. global $dictionary;
  383. if (isset($dictionary[$this->getObjectName()]['audited']))
  384. {
  385. return $dictionary[$this->getObjectName()]['audited'];
  386. }
  387. else
  388. {
  389. return false;
  390. }
  391. }
  392. /**
  393. * Returns the name of the audit table.
  394. * Audit table's name is based on implementing class' table name.
  395. *
  396. * @return String Audit table name.
  397. *
  398. * Internal function, do not override.
  399. */
  400. function get_audit_table_name()
  401. {
  402. return $this->getTableName().'_audit';
  403. }
  404. /**
  405. * Returns the name of the custom table.
  406. * Custom table's name is based on implementing class' table name.
  407. *
  408. * @return String Custom table name.
  409. *
  410. * Internal function, do not override.
  411. */
  412. public function get_custom_table_name()
  413. {
  414. return $this->getTableName().'_cstm';
  415. }
  416. /**
  417. * If auditing is enabled, create the audit table.
  418. *
  419. * Function is used by the install scripts and a repair utility in the admin panel.
  420. *
  421. * Internal function, do not override.
  422. */
  423. function create_audit_table()
  424. {
  425. global $dictionary;
  426. $table_name=$this->get_audit_table_name();
  427. require('metadata/audit_templateMetaData.php');
  428. // Bug: 52583 Need ability to customize template for audit tables
  429. $custom = 'custom/metadata/audit_templateMetaData_' . $this->getTableName() . '.php';
  430. if (file_exists($custom))
  431. {
  432. require($custom);
  433. }
  434. $fieldDefs = $dictionary['audit']['fields'];
  435. $indices = $dictionary['audit']['indices'];
  436. // Renaming template indexes to fit the particular audit table (removed the brittle hard coding)
  437. foreach($indices as $nr => $properties){
  438. $indices[$nr]['name'] = 'idx_' . strtolower($this->getTableName()) . '_' . $properties['name'];
  439. }
  440. $engine = null;
  441. if(isset($dictionary['audit']['engine'])) {
  442. $engine = $dictionary['audit']['engine'];
  443. } else if(isset($dictionary[$this->getObjectName()]['engine'])) {
  444. $engine = $dictionary[$this->getObjectName()]['engine'];
  445. }
  446. $this->db->createTableParams($table_name, $fieldDefs, $indices, $engine);
  447. }
  448. /**
  449. * Returns the implementing class' table name.
  450. *
  451. * All implementing classes set a value for the table_name variable. This value is returned as the
  452. * table name. If not set, table name is extracted from the implementing module's vardef.
  453. *
  454. * @return String Table name.
  455. *
  456. * Internal function, do not override.
  457. */
  458. public function getTableName()
  459. {
  460. if(isset($this->table_name))
  461. {
  462. return $this->table_name;
  463. }
  464. global $dictionary;
  465. return $dictionary[$this->getObjectName()]['table'];
  466. }
  467. /**
  468. * Returns field definitions for the implementing module.
  469. *
  470. * The definitions were loaded in the constructor.
  471. *
  472. * @return Array Field definitions.
  473. *
  474. * Internal function, do not override.
  475. */
  476. function getFieldDefinitions()
  477. {
  478. return $this->field_defs;
  479. }
  480. /**
  481. * Returns index definitions for the implementing module.
  482. *
  483. * The definitions were loaded in the constructor.
  484. *
  485. * @return Array Index definitions.
  486. *
  487. * Internal function, do not override.
  488. */
  489. function getIndices()
  490. {
  491. global $dictionary;
  492. if(isset($dictionary[$this->getObjectName()]['indices']))
  493. {
  494. return $dictionary[$this->getObjectName()]['indices'];
  495. }
  496. return array();
  497. }
  498. /**
  499. * Returns field definition for the requested field name.
  500. *
  501. * The definitions were loaded in the constructor.
  502. *
  503. * @param string field name,
  504. * @return Array Field properties or boolean false if the field doesn't exist
  505. *
  506. * Internal function, do not override.
  507. */
  508. function getFieldDefinition($name)
  509. {
  510. if ( !isset($this->field_defs[$name]) )
  511. return false;
  512. return $this->field_defs[$name];
  513. }
  514. /**
  515. * Returnss definition for the id field name.
  516. *
  517. * The definitions were loaded in the constructor.
  518. *
  519. * @return Array Field properties.
  520. *
  521. * Internal function, do not override.
  522. */
  523. function getPrimaryFieldDefinition()
  524. {
  525. $def = $this->getFieldDefinition("id");
  526. if(empty($def)) {
  527. $def = $this->getFieldDefinition(0);
  528. }
  529. if (empty($def)) {
  530. $defs = $this->field_defs;
  531. reset($defs);
  532. $def = current($defs);
  533. }
  534. return $def;
  535. }
  536. /**
  537. * Returns the value for the requested field.
  538. *
  539. * When a row of data is fetched using the bean, all fields are created as variables in the context
  540. * of the bean and then fetched values are set in these variables.
  541. *
  542. * @param string field name,
  543. * @return varies Field value.
  544. *
  545. * Internal function, do not override.
  546. */
  547. function getFieldValue($name)
  548. {
  549. if (!isset($this->$name)){
  550. return FALSE;
  551. }
  552. if($this->$name === TRUE){
  553. return 1;
  554. }
  555. if($this->$name === FALSE){
  556. return 0;
  557. }
  558. return $this->$name;
  559. }
  560. /**
  561. * Basically undoes the effects of SugarBean::populateDefaultValues(); this method is best called right after object
  562. * initialization.
  563. */
  564. public function unPopulateDefaultValues()
  565. {
  566. if ( !is_array($this->field_defs) )
  567. return;
  568. foreach ($this->field_defs as $field => $value) {
  569. if( !empty($this->$field)
  570. && ((isset($value['default']) && $this->$field == $value['default']) || (!empty($value['display_default']) && $this->$field == $value['display_default']))
  571. ) {
  572. $this->$field = null;
  573. continue;
  574. }
  575. if(!empty($this->$field) && !empty($value['display_default']) && in_array($value['type'], array('date', 'datetime', 'datetimecombo')) &&
  576. $this->$field == $this->parseDateDefault($value['display_default'], ($value['type'] != 'date'))) {
  577. $this->$field = null;
  578. }
  579. }
  580. }
  581. /**
  582. * Create date string from default value
  583. * like '+1 month'
  584. * @param string $value
  585. * @param bool $time Should be expect time set too?
  586. * @return string
  587. */
  588. protected function parseDateDefault($value, $time = false)
  589. {
  590. global $timedate;
  591. if($time) {
  592. $dtAry = explode('&', $value, 2);
  593. $dateValue = $timedate->getNow(true)->modify($dtAry[0]);
  594. if(!empty($dtAry[1])) {
  595. $timeValue = $timedate->fromString($dtAry[1]);
  596. $dateValue->setTime($timeValue->hour, $timeValue->min, $timeValue->sec);
  597. }
  598. return $timedate->asUser($dateValue);
  599. } else {
  600. return $timedate->asUserDate($timedate->getNow(true)->modify($value));
  601. }
  602. }
  603. function populateDefaultValues($force=false){
  604. if ( !is_array($this->field_defs) )
  605. return;
  606. foreach($this->field_defs as $field=>$value){
  607. if((isset($value['default']) || !empty($value['display_default'])) && ($force || empty($this->$field))){
  608. $type = $value['type'];
  609. switch($type){
  610. case 'date':
  611. if(!empty($value['display_default'])){
  612. $this->$field = $this->parseDateDefault($value['display_default']);
  613. }
  614. break;
  615. case 'datetime':
  616. case 'datetimecombo':
  617. if(!empty($value['display_default'])){
  618. $this->$field = $this->parseDateDefault($value['display_default'], true);
  619. }
  620. break;
  621. case 'multienum':
  622. if(empty($value['default']) && !empty($value['display_default']))
  623. $this->$field = $value['display_default'];
  624. else
  625. $this->$field = $value['default'];
  626. break;
  627. case 'bool':
  628. if(isset($this->$field)){
  629. break;
  630. }
  631. default:
  632. if ( isset($value['default']) && $value['default'] !== '' ) {
  633. $this->$field = htmlentities($value['default'], ENT_QUOTES, 'UTF-8');
  634. } else {
  635. $this->$field = '';
  636. }
  637. } //switch
  638. }
  639. } //foreach
  640. }
  641. /**
  642. * Removes relationship metadata cache.
  643. *
  644. * Every module that has relationships defined with other modules, has this meta data cached. The cache is
  645. * stores in 2 locations: relationships table and file system. This method clears the cache from both locations.
  646. *
  647. * @param string $key module whose meta cache is to be cleared.
  648. * @param string $db database handle.
  649. * @param string $tablename table name
  650. * @param string $dictionary vardef for the module
  651. * @param string $module_dir name of subdirectory where module is installed.
  652. *
  653. * @return Nothing
  654. * @static
  655. *
  656. * Internal function, do not override.
  657. */
  658. function removeRelationshipMeta($key,$db,$tablename,$dictionary,$module_dir)
  659. {
  660. //load the module dictionary if not supplied.
  661. if ((!isset($dictionary) or empty($dictionary)) && !empty($module_dir))
  662. {
  663. $filename='modules/'. $module_dir . '/vardefs.php';
  664. if(file_exists($filename))
  665. {
  666. include($filename);
  667. }
  668. }
  669. if (!is_array($dictionary) or !array_key_exists($key, $dictionary))
  670. {
  671. $GLOBALS['log']->fatal("removeRelationshipMeta: Metadata for table ".$tablename. " does not exist");
  672. display_notice("meta data absent for table ".$tablename." keyed to $key ");
  673. }
  674. else
  675. {
  676. if (isset($dictionary[$key]['relationships']))
  677. {
  678. $RelationshipDefs = $dictionary[$key]['relationships'];
  679. foreach ($RelationshipDefs as $rel_name)
  680. {
  681. Relationship::delete($rel_name,$db);
  682. }
  683. }
  684. }
  685. }
  686. /**
  687. * This method has been deprecated.
  688. *
  689. * @see removeRelationshipMeta()
  690. * @deprecated 4.5.1 - Nov 14, 2006
  691. * @static
  692. */
  693. function remove_relationship_meta($key,$db,$log,$tablename,$dictionary,$module_dir)
  694. {
  695. SugarBean::removeRelationshipMeta($key,$db,$tablename,$dictionary,$module_dir);
  696. }
  697. /**
  698. * Populates the relationship meta for a module.
  699. *
  700. * It is called during setup/install. It is used statically to create relationship meta data for many-to-many tables.
  701. *
  702. * @param string $key name of the object.
  703. * @param object $db database handle.
  704. * @param string $tablename table, meta data is being populated for.
  705. * @param array dictionary vardef dictionary for the object. *
  706. * @param string module_dir name of subdirectory where module is installed.
  707. * @param boolean $iscustom Optional,set to true if module is installed in a custom directory. Default value is false.
  708. * @static
  709. *
  710. * Internal function, do not override.
  711. */
  712. function createRelationshipMeta($key,$db,$tablename,$dictionary,$module_dir,$iscustom=false)
  713. {
  714. //load the module dictionary if not supplied.
  715. if (empty($dictionary) && !empty($module_dir))
  716. {
  717. if($iscustom)
  718. {
  719. $filename='custom/modules/' . $module_dir . '/Ext/Vardefs/vardefs.ext.php';
  720. }
  721. else
  722. {
  723. if ($key == 'User')
  724. {
  725. // a very special case for the Employees module
  726. // this must be done because the Employees/vardefs.php does an include_once on
  727. // Users/vardefs.php
  728. $filename='modules/Users/vardefs.php';
  729. }
  730. else
  731. {
  732. $filename='modules/'. $module_dir . '/vardefs.php';
  733. }
  734. }
  735. if(file_exists($filename))
  736. {
  737. include($filename);
  738. // cn: bug 7679 - dictionary entries defined as $GLOBALS['name'] not found
  739. if(empty($dictionary) || !empty($GLOBALS['dictionary'][$key]))
  740. {
  741. $dictionary = $GLOBALS['dictionary'];
  742. }
  743. }
  744. else
  745. {
  746. $GLOBALS['log']->debug("createRelationshipMeta: no metadata file found" . $filename);
  747. return;
  748. }
  749. }
  750. if (!is_array($dictionary) or !array_key_exists($key, $dictionary))
  751. {
  752. $GLOBALS['log']->fatal("createRelationshipMeta: Metadata for table ".$tablename. " does not exist");
  753. display_notice("meta data absent for table ".$tablename." keyed to $key ");
  754. }
  755. else
  756. {
  757. if (isset($dictionary[$key]['relationships']))
  758. {
  759. $RelationshipDefs = $dictionary[$key]['relationships'];
  760. $delimiter=',';
  761. global $beanList;
  762. $beanList_ucase=array_change_key_case ( $beanList ,CASE_UPPER);
  763. foreach ($RelationshipDefs as $rel_name=>$rel_def)
  764. {
  765. if (isset($rel_def['lhs_module']) and !isset($beanList_ucase[strtoupper($rel_def['lhs_module'])])) {
  766. $GLOBALS['log']->debug('skipping orphaned relationship record ' . $rel_name . ' lhs module is missing ' . $rel_def['lhs_module']);
  767. continue;
  768. }
  769. if (isset($rel_def['rhs_module']) and !isset($beanList_ucase[strtoupper($rel_def['rhs_module'])])) {
  770. $GLOBALS['log']->debug('skipping orphaned relationship record ' . $rel_name . ' rhs module is missing ' . $rel_def['rhs_module']);
  771. continue;
  772. }
  773. //check whether relationship exists or not first.
  774. if (Relationship::exists($rel_name,$db))
  775. {
  776. $GLOBALS['log']->debug('Skipping, reltionship already exists '.$rel_name);
  777. }
  778. else
  779. {
  780. $seed = BeanFactory::getBean("Relationships");
  781. $keys = array_keys($seed->field_defs);
  782. $toInsert = array();
  783. foreach($keys as $key)
  784. {
  785. if ($key == "id")
  786. {
  787. $toInsert[$key] = create_guid();
  788. }
  789. else if ($key == "relationship_name")
  790. {
  791. $toInsert[$key] = $rel_name;
  792. }
  793. else if (isset($rel_def[$key]))
  794. {
  795. $toInsert[$key] = $rel_def[$key];
  796. }
  797. //todo specify defaults if meta not defined.
  798. }
  799. $column_list = implode(",", array_keys($toInsert));
  800. $value_list = "'" . implode("','", array_values($toInsert)) . "'";
  801. //create the record. todo add error check.
  802. $insert_string = "INSERT into relationships (" .$column_list. ") values (".$value_list.")";
  803. $db->query($insert_string, true);
  804. }
  805. }
  806. }
  807. else
  808. {
  809. //todo
  810. //log informational message stating no relationships meta was set for this bean.
  811. }
  812. }
  813. }
  814. /**
  815. * This method has been deprecated.
  816. * @see createRelationshipMeta()
  817. * @deprecated 4.5.1 - Nov 14, 2006
  818. * @static
  819. */
  820. function create_relationship_meta($key,&$db,&$log,$tablename,$dictionary,$module_dir)
  821. {
  822. SugarBean::createRelationshipMeta($key,$db,$tablename,$dictionary,$module_dir);
  823. }
  824. /**
  825. * Handle the following when a SugarBean object is cloned
  826. *
  827. * Currently all this does it unset any relationships that were created prior to cloning the object
  828. *
  829. * @api
  830. */
  831. public function __clone()
  832. {
  833. if(!empty($this->loaded_relationships)) {
  834. foreach($this->loaded_relationships as $rel) {
  835. unset($this->$rel);
  836. }
  837. }
  838. }
  839. /**
  840. * Loads the request relationship. This method should be called before performing any operations on the related data.
  841. *
  842. * This method searches the vardef array for the requested attribute's definition. If the attribute is of the type
  843. * link then it creates a similary named variable and loads the relationship definition.
  844. *
  845. * @param string $rel_name relationship/attribute name.
  846. * @return nothing.
  847. */
  848. function load_relationship($rel_name)
  849. {
  850. $GLOBALS['log']->debug("SugarBean[{$this->object_name}].load_relationships, Loading relationship (".$rel_name.").");
  851. if (empty($rel_name))
  852. {
  853. $GLOBALS['log']->error("SugarBean.load_relationships, Null relationship name passed.");
  854. return false;
  855. }
  856. $fieldDefs = $this->getFieldDefinitions();
  857. //find all definitions of type link.
  858. if (!empty($fieldDefs[$rel_name]))
  859. {
  860. //initialize a variable of type Link
  861. require_once('data/Link2.php');
  862. $class = load_link_class($fieldDefs[$rel_name]);
  863. if (isset($this->$rel_name) && $this->$rel_name instanceof $class) {
  864. return true;
  865. }
  866. //if rel_name is provided, search the fieldef array keys by name.
  867. if (isset($fieldDefs[$rel_name]['type']) && $fieldDefs[$rel_name]['type'] == 'link')
  868. {
  869. if ($class == "Link2")
  870. $this->$rel_name = new $class($rel_name, $this);
  871. else
  872. $this->$rel_name = new $class($fieldDefs[$rel_name]['relationship'], $this, $fieldDefs[$rel_name]);
  873. if (empty($this->$rel_name) ||
  874. (method_exists($this->$rel_name, "loadedSuccesfully") && !$this->$rel_name->loadedSuccesfully()))
  875. {
  876. unset($this->$rel_name);
  877. return false;
  878. }
  879. // keep track of the loaded relationships
  880. $this->loaded_relationships[] = $rel_name;
  881. return true;
  882. }
  883. }
  884. $GLOBALS['log']->debug("SugarBean.load_relationships, Error Loading relationship (".$rel_name.")");
  885. return false;
  886. }
  887. /**
  888. * Loads all attributes of type link.
  889. *
  890. * DO NOT CALL THIS FUNCTION IF YOU CAN AVOID IT. Please use load_relationship directly instead.
  891. *
  892. * Method searches the implmenting module's vardef file for attributes of type link, and for each attribute
  893. * create a similary named variable and load the relationship definition.
  894. *
  895. * @return Nothing
  896. *
  897. * Internal function, do not override.
  898. */
  899. function load_relationships()
  900. {
  901. $GLOBALS['log']->debug("SugarBean.load_relationships, Loading all relationships of type link.");
  902. $linked_fields=$this->get_linked_fields();
  903. foreach($linked_fields as $name=>$properties)
  904. {
  905. $this->load_relationship($name);
  906. }
  907. }
  908. /**
  909. * Returns an array of beans of related data.
  910. *
  911. * For instance, if an account is related to 10 contacts , this function will return an array of contacts beans (10)
  912. * with each bean representing a contact record.
  913. * Method will load the relationship if not done so already.
  914. *
  915. * @param string $field_name relationship to be loaded.
  916. * @param string $bean name class name of the related bean.
  917. * @param array $sort_array optional, unused
  918. * @param int $begin_index Optional, default 0, unused.
  919. * @param int $end_index Optional, default -1
  920. * @param int $deleted Optional, Default 0, 0 adds deleted=0 filter, 1 adds deleted=1 filter.
  921. * @param string $optional_where, Optional, default empty.
  922. *
  923. * Internal function, do not override.
  924. */
  925. function get_linked_beans($field_name,$bean_name, $sort_array = array(), $begin_index = 0, $end_index = -1,
  926. $deleted=0, $optional_where="")
  927. {
  928. //if bean_name is Case then use aCase
  929. if($bean_name=="Case")
  930. $bean_name = "aCase";
  931. if($this->load_relationship($field_name)) {
  932. if ($this->$field_name instanceof Link) {
  933. // some classes are still based on Link, e.g. TeamSetLink
  934. return array_values($this->$field_name->getBeans(new $bean_name(), $sort_array, $begin_index, $end_index, $deleted, $optional_where));
  935. } else {
  936. // Link2 style
  937. if ($end_index != -1 || !empty($deleted) || !empty($optional_where))
  938. return array_values($this->$field_name->getBeans(array(
  939. 'where' => $optional_where,
  940. 'deleted' => $deleted,
  941. 'limit' => ($end_index - $begin_index)
  942. )));
  943. else
  944. return array_values($this->$field_name->getBeans());
  945. }
  946. }
  947. else
  948. return array();
  949. }
  950. /**
  951. * Returns an array of fields that are of type link.
  952. *
  953. * @return array List of fields.
  954. *
  955. * Internal function, do not override.
  956. */
  957. function get_linked_fields()
  958. {
  959. $linked_fields=array();
  960. // require_once('data/Link.php');
  961. $fieldDefs = $this->getFieldDefinitions();
  962. //find all definitions of type link.
  963. if (!empty($fieldDefs))
  964. {
  965. foreach ($fieldDefs as $name=>$properties)
  966. {
  967. if (array_search('link',$properties) === 'type')
  968. {
  969. $linked_fields[$name]=$properties;
  970. }
  971. }
  972. }
  973. return $linked_fields;
  974. }
  975. /**
  976. * Returns an array of fields that are able to be Imported into
  977. * i.e. 'importable' not set to 'false'
  978. *
  979. * @return array List of fields.
  980. *
  981. * Internal function, do not override.
  982. */
  983. function get_importable_fields()
  984. {
  985. $importableFields = array();
  986. $fieldDefs= $this->getFieldDefinitions();
  987. if (!empty($fieldDefs)) {
  988. foreach ($fieldDefs as $key=>$value_array) {
  989. if ( (isset($value_array['importable'])
  990. && (is_string($value_array['importable']) && $value_array['importable'] == 'false'
  991. || is_bool($value_array['importable']) && $value_array['importable'] == false))
  992. || (isset($value_array['type']) && $value_array['type'] == 'link')
  993. || (isset($value_array['auto_increment'])
  994. && ($value_array['type'] == true || $value_array['type'] == 'true')) ) {
  995. // only allow import if we force it
  996. if (isset($value_array['importable'])
  997. && (is_string($value_array['importable']) && $value_array['importable'] == 'true'
  998. || is_bool($value_array['importable']) && $value_array['importable'] == true)) {
  999. $importableFields[$key]=$value_array;
  1000. }
  1001. }
  1002. else {
  1003. $importableFields[$key]=$value_array;
  1004. }
  1005. }
  1006. }
  1007. return $importableFields;
  1008. }
  1009. /**
  1010. * Returns an array of fields that are of type relate.
  1011. *
  1012. * @return array List of fields.
  1013. *
  1014. * Internal function, do not override.
  1015. */
  1016. function get_related_fields()
  1017. {
  1018. $related_fields=array();
  1019. // require_once('data/Link.php');
  1020. $fieldDefs = $this->getFieldDefinitions();
  1021. //find all definitions of type link.
  1022. if (!empty($fieldDefs))
  1023. {
  1024. foreach ($fieldDefs as $name=>$properties)
  1025. {
  1026. if (array_search('relate',$properties) === 'type')
  1027. {
  1028. $related_fields[$name]=$properties;
  1029. }
  1030. }
  1031. }
  1032. return $related_fields;
  1033. }
  1034. /**
  1035. * Returns an array of fields that are required for import
  1036. *
  1037. * @return array
  1038. */
  1039. function get_import_required_fields()
  1040. {
  1041. $importable_fields = $this->get_importable_fields();
  1042. $required_fields = array();
  1043. foreach ( $importable_fields as $name => $properties ) {
  1044. if ( isset($properties['importable']) && is_string($properties['importable']) && $properties['importable'] == 'required' ) {
  1045. $required_fields[$name] = $properties;
  1046. }
  1047. }
  1048. return $required_fields;
  1049. }
  1050. /**
  1051. * Iterates through all the relationships and deletes all records for reach relationship.
  1052. *
  1053. * @param string $id Primary key value of the parent reocrd
  1054. */
  1055. function delete_linked($id)
  1056. {
  1057. $linked_fields=$this->get_linked_fields();
  1058. foreach ($linked_fields as $name => $value)
  1059. {
  1060. if ($this->load_relationship($name))
  1061. {
  1062. $this->$name->delete($id);
  1063. }
  1064. else
  1065. {
  1066. $GLOBALS['log']->fatal("error loading relationship $name");
  1067. }
  1068. }
  1069. }
  1070. /**
  1071. * Creates tables for the module implementing the class.
  1072. * If you override this function make sure that your code can handles table creation.
  1073. *
  1074. */
  1075. function create_tables()
  1076. {
  1077. global $dictionary;
  1078. $key = $this->getObjectName();
  1079. if (!array_key_exists($key, $dictionary))
  1080. {
  1081. $GLOBALS['log']->fatal("create_tables: Metadata for table ".$this->table_name. " does not exist");
  1082. display_notice("meta data absent for table ".$this->table_name." keyed to $key ");
  1083. }
  1084. else
  1085. {
  1086. if(!$this->db->tableExists($this->table_name))
  1087. {
  1088. $this->db->createTable($this);
  1089. if($this->bean_implements('ACL')){
  1090. if(!empty($this->acltype)){
  1091. ACLAction::addActions($this->getACLCategory(), $this->acltype);
  1092. }else{
  1093. ACLAction::addActions($this->getACLCategory());
  1094. }
  1095. }
  1096. }
  1097. else
  1098. {
  1099. echo "Table already exists : $this->table_name<br>";
  1100. }
  1101. if($this->is_AuditEnabled()){
  1102. if (!$this->db->tableExists($this->get_audit_table_name())) {
  1103. $this->create_audit_table();
  1104. }
  1105. }
  1106. }
  1107. }
  1108. /**
  1109. * Delete the primary table for the module implementing the class.
  1110. * If custom fields were added to this table/module, the custom table will be removed too, along with the cache
  1111. * entries that define the custom fields.
  1112. *
  1113. */
  1114. function drop_tables()
  1115. {
  1116. global $dictionary;
  1117. $key = $this->getObjectName();
  1118. if (!array_key_exists($key, $dictionary))
  1119. {
  1120. $GLOBALS['log']->fatal("drop_tables: Metadata for table ".$this->table_name. " does not exist");
  1121. echo "meta data absent for table ".$this->table_name."<br>\n";
  1122. } else {
  1123. if(empty($this->table_name))return;
  1124. if ($this->db->tableExists($this->table_name))
  1125. $this->db->dropTable($this);
  1126. if ($this->db->tableExists($this->table_name. '_cstm'))
  1127. {
  1128. $this->db->dropTableName($this->table_name. '_cstm');
  1129. DynamicField::deleteCache();
  1130. }
  1131. if ($this->db->tableExists($this->get_audit_table_name())) {
  1132. $this->db->dropTableName($this->get_audit_table_name());
  1133. }
  1134. }
  1135. }
  1136. /**
  1137. * Loads the definition of custom fields defined for the module.
  1138. * Local file system cache is created as needed.
  1139. *
  1140. * @param string $module_name setting up custom fields for this module.
  1141. * @param boolean $clean_load Optional, default true, rebuilds the cache if set to true.
  1142. */
  1143. function setupCustomFields($module_name, $clean_load=true)
  1144. {
  1145. $this->custom_fields = new DynamicField($module_name);
  1146. $this->custom_fields->setup($this);
  1147. }
  1148. /**
  1149. * Cleans char, varchar, text, etc. fields of XSS type materials
  1150. */
  1151. function cleanBean() {
  1152. foreach($this->field_defs as $key => $def) {
  1153. if (isset($def['type'])) {
  1154. $type=$def['type'];
  1155. }
  1156. if(isset($def['dbType']))
  1157. $type .= $def['dbType'];
  1158. if($def['type'] == 'html') {
  1159. $this->$key = SugarCleaner::cleanHtml($this->$key, true);
  1160. } elseif((strpos($type, 'char') !== false ||
  1161. strpos($type, 'text') !== false ||
  1162. $type == 'enum') &&
  1163. !empty($this->$key)
  1164. ) {
  1165. $this->$key = SugarCleaner::cleanHtml($this->$key);
  1166. }
  1167. }
  1168. }
  1169. /**
  1170. * Implements a generic insert and update logic for any SugarBean
  1171. * This method only works for subclasses that implement the same variable names.
  1172. * This method uses the presence of an id field that is not null to signify and update.
  1173. * The id field should not be set otherwise.
  1174. *
  1175. * @param boolean $check_notify Optional, default false, if set to true assignee of the record is notified via email.
  1176. * @todo Add support for field type validation and encoding of parameters.
  1177. */
  1178. function save($check_notify = FALSE)
  1179. {
  1180. $this->in_save = true;
  1181. // cn: SECURITY - strip XSS potential vectors
  1182. $this->cleanBean();
  1183. // This is used so custom/3rd-party code can be upgraded with fewer issues, this will be removed in a future release
  1184. $this->fixUpFormatting();
  1185. global $timedate;
  1186. global $current_user, $action;
  1187. $isUpdate = true;
  1188. if(empty($this->id))
  1189. {
  1190. $isUpdate = false;
  1191. }
  1192. if ( $this->new_with_id == true )
  1193. {
  1194. $isUpdate = false;
  1195. }
  1196. if(empty($this->date_modified) || $this->update_date_modified)
  1197. {
  1198. $this->date_modified = $GLOBALS['timedate']->nowDb();
  1199. }
  1200. $this->_checkOptimisticLocking($action, $isUpdate);
  1201. if(!empty($this->modified_by_name)) $this->old_modified_by_name = $this->modified_by_name;
  1202. if($this->update_modified_by)
  1203. {
  1204. $this->modified_user_id = 1;
  1205. if (!empty($current_user))
  1206. {
  1207. $this->modified_user_id = $current_user->id;
  1208. $this->modified_by_name = $current_user->user_name;
  1209. }
  1210. }
  1211. if ($this->deleted != 1)
  1212. $this->deleted = 0;
  1213. if(!$isUpdate)
  1214. {
  1215. if (empty($this->date_entered))
  1216. {
  1217. $this->date_entered = $this->date_modified;
  1218. }
  1219. if($this->set_created_by == true)
  1220. {
  1221. // created by should always be this user
  1222. $this->created_by = (isset($current_user)) ? $current_user->id : "";
  1223. }
  1224. if( $this->new_with_id == false)
  1225. {
  1226. $this->id = create_guid();
  1227. }
  1228. }
  1229. require_once("data/BeanFactory.php");
  1230. BeanFactory::registerBean($this->module_name, $this);
  1231. if (empty($GLOBALS['updating_relationships']) && empty($GLOBALS['saving_relationships']) && empty ($GLOBALS['resavingRelatedBeans']))
  1232. {
  1233. $GLOBALS['saving_relationships'] = true;
  1234. // let subclasses save related field changes
  1235. $this->save_relationship_changes($isUpdate);
  1236. $GLOBALS['saving_relationships'] = false;
  1237. }
  1238. if($isUpdate && !$this->update_date_entered)
  1239. {
  1240. unset($this->date_entered);
  1241. }
  1242. // call the custom business logic
  1243. $custom_logic_arguments['check_notify'] = $check_notify;
  1244. $this->call_custom_logic("before_save", $custom_logic_arguments);
  1245. unset($custom_logic_arguments);
  1246. if(isset($this->custom_fields))
  1247. {
  1248. $this->custom_fields->bean = $this;
  1249. $this->custom_fields->save($isUpdate);
  1250. }
  1251. // use the db independent query generator
  1252. $this->preprocess_fields_on_save();
  1253. //construct the SQL to create the audit record if auditing is enabled.
  1254. $dataChanges=array();
  1255. if ($this->is_AuditEnabled()) {
  1256. if ($isUpdate && !isset($this->fetched_row)) {
  1257. $GLOBALS['log']->debug('Auditing: Retrieve was not called, audit record will not be created.');
  1258. } else {
  1259. $dataChanges=$this->db->getDataChanges($this);
  1260. }
  1261. }
  1262. $this->_sendNotifications($check_notify);
  1263. if ($isUpdate) {
  1264. $this->db->update($this);
  1265. } else {
  1266. $this->db->insert($this);
  1267. }
  1268. if (!empty($dataChanges) && is_array($dataChanges))
  1269. {
  1270. foreach ($dataChanges as $change)
  1271. {
  1272. $this->db->save_audit_records($this,$change);
  1273. }
  1274. }
  1275. if (empty($GLOBALS['resavingRelatedBeans'])){
  1276. SugarRelationship::resaveRelatedBeans();
  1277. }
  1278. //If we aren't in setup mode and we have a current user and module, then we track
  1279. if(isset($GLOBALS['current_user']) && isset($this->module_dir))
  1280. {
  1281. $this->track_view($current_user->id, $this->modul

Large files files are truncated, but you can click here to view the full file