PageRenderTime 30ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 1ms

/data/SugarBean.php

https://github.com/vincentamari/SuperSweetAdmin
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

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

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