PageRenderTime 76ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/Users/User.php

https://bitbucket.org/cviolette/sugarcrm
PHP | 1857 lines | 1162 code | 258 blank | 437 comment | 228 complexity | bff839e28f1a81ccbb04d2e86c2efc20 MD5 | raw file
Possible License(s): LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause

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: TODO: To be written.
  39. * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc.
  40. * All Rights Reserved.
  41. * Contributor(s): ______________________________________..
  42. ********************************************************************************/
  43. require_once('include/SugarObjects/templates/person/Person.php');
  44. // User is used to store customer information.
  45. class User extends Person {
  46. // Stored fields
  47. var $name = '';
  48. var $full_name;
  49. var $id;
  50. var $user_name;
  51. var $user_hash;
  52. var $salutation;
  53. var $first_name;
  54. var $last_name;
  55. var $date_entered;
  56. var $date_modified;
  57. var $modified_user_id;
  58. var $created_by;
  59. var $created_by_name;
  60. var $modified_by_name;
  61. var $description;
  62. var $phone_home;
  63. var $phone_mobile;
  64. var $phone_work;
  65. var $phone_other;
  66. var $phone_fax;
  67. var $email1;
  68. var $email2;
  69. var $address_street;
  70. var $address_city;
  71. var $address_state;
  72. var $address_postalcode;
  73. var $address_country;
  74. var $status;
  75. var $title;
  76. var $portal_only;
  77. var $department;
  78. var $authenticated = false;
  79. var $error_string;
  80. var $is_admin;
  81. var $employee_status;
  82. var $messenger_id;
  83. var $messenger_type;
  84. var $is_group;
  85. var $accept_status; // to support Meetings
  86. //adding a property called team_id so we can populate it for use in the team widget
  87. var $team_id;
  88. var $receive_notifications;
  89. var $reports_to_name;
  90. var $reports_to_id;
  91. var $team_exists = false;
  92. var $table_name = "users";
  93. var $module_dir = 'Users';
  94. var $object_name = "User";
  95. var $user_preferences;
  96. var $importable = true;
  97. var $_userPreferenceFocus;
  98. var $encodeFields = Array ("first_name", "last_name", "description");
  99. // This is used to retrieve related fields from form posts.
  100. var $additional_column_fields = array ('reports_to_name'
  101. );
  102. var $emailAddress;
  103. var $new_schema = true;
  104. function User() {
  105. parent::Person();
  106. $this->_loadUserPreferencesFocus();
  107. }
  108. protected function _loadUserPreferencesFocus()
  109. {
  110. $this->_userPreferenceFocus = new UserPreference($this);
  111. }
  112. /**
  113. * returns an admin user
  114. */
  115. public function getSystemUser()
  116. {
  117. if (null === $this->retrieve('1'))
  118. // handle cases where someone deleted user with id "1"
  119. $this->retrieve_by_string_fields(array(
  120. 'status' => 'Active',
  121. 'is_admin' => '1',
  122. ));
  123. return $this;
  124. }
  125. /**
  126. * convenience function to get user's default signature
  127. */
  128. function getDefaultSignature() {
  129. if($defaultId = $this->getPreference('signature_default')) {
  130. return $this->getSignature($defaultId);
  131. } else {
  132. return array();
  133. }
  134. }
  135. /**
  136. * retrieves the signatures for a user
  137. * @param string id ID of user_signature
  138. * @return array ID, signature, and signature_html
  139. */
  140. public function getSignature($id)
  141. {
  142. $signatures = $this->getSignaturesArray();
  143. return isset($signatures[$id]) ? $signatures[$id] : FALSE;
  144. }
  145. function getSignaturesArray() {
  146. $q = 'SELECT * FROM users_signatures WHERE user_id = \''.$this->id.'\' AND deleted = 0 ORDER BY name ASC';
  147. $r = $this->db->query($q);
  148. // provide "none"
  149. $sig = array(""=>"");
  150. while($a = $this->db->fetchByAssoc($r)) {
  151. $sig[$a['id']] = $a;
  152. }
  153. return $sig;
  154. }
  155. /**
  156. * retrieves any signatures that the User may have created as <select>
  157. */
  158. public function getSignatures(
  159. $live = false,
  160. $defaultSig = '',
  161. $forSettings = false
  162. )
  163. {
  164. $sig = $this->getSignaturesArray();
  165. $sigs = array();
  166. foreach ($sig as $key => $arr)
  167. {
  168. $sigs[$key] = !empty($arr['name']) ? $arr['name'] : '';
  169. }
  170. $change = '';
  171. if(!$live) {
  172. $change = ($forSettings) ? "onChange='displaySignatureEdit();'" : "onChange='setSigEditButtonVisibility();'";
  173. }
  174. $id = (!$forSettings) ? 'signature_id' : 'signature_idDisplay';
  175. $out = "<select {$change} id='{$id}' name='{$id}'>";
  176. $out .= get_select_options_with_id($sigs, $defaultSig).'</select>';
  177. return $out;
  178. }
  179. /**
  180. * returns buttons and JS for signatures
  181. */
  182. function getSignatureButtons($jscall='', $defaultDisplay=false) {
  183. global $mod_strings;
  184. $jscall = empty($jscall) ? 'open_email_signature_form' : $jscall;
  185. $butts = "<input class='button' onclick='javascript:{$jscall}(\"\", \"{$this->id}\");' value='{$mod_strings['LBL_BUTTON_CREATE']}' type='button'>&nbsp;";
  186. if($defaultDisplay) {
  187. $butts .= '<span name="edit_sig" id="edit_sig" style="visibility:inherit;"><input class="button" onclick="javascript:'.$jscall.'(document.getElementById(\'signature_id\', \'\').value)" value="'.$mod_strings['LBL_BUTTON_EDIT'].'" type="button" tabindex="392">&nbsp;
  188. </span>';
  189. } else {
  190. $butts .= '<span name="edit_sig" id="edit_sig" style="visibility:hidden;"><input class="button" onclick="javascript:'.$jscall.'(document.getElementById(\'signature_id\', \'\').value)" value="'.$mod_strings['LBL_BUTTON_EDIT'].'" type="button" tabindex="392">&nbsp;
  191. </span>';
  192. }
  193. return $butts;
  194. }
  195. /**
  196. * performs a rudimentary check to verify if a given user has setup personal
  197. * InboundEmail
  198. *
  199. * @return bool
  200. */
  201. public function hasPersonalEmail()
  202. {
  203. $focus = new InboundEmail;
  204. $focus->retrieve_by_string_fields(array('group_id' => $this->id));
  205. return !empty($focus->id);
  206. }
  207. /* Returns the User's private GUID; this is unassociated with the User's
  208. * actual GUID. It is used to secure file names that must be HTTP://
  209. * accesible, but obfusicated.
  210. */
  211. function getUserPrivGuid() {
  212. $userPrivGuid = $this->getPreference('userPrivGuid', 'global', $this);
  213. if ($userPrivGuid) {
  214. return $userPrivGuid;
  215. } else {
  216. $this->setUserPrivGuid();
  217. if (!isset ($_SESSION['setPrivGuid'])) {
  218. $_SESSION['setPrivGuid'] = true;
  219. $userPrivGuid = $this->getUserPrivGuid();
  220. return $userPrivGuid;
  221. } else {
  222. sugar_die("Breaking Infinite Loop Condition: Could not setUserPrivGuid.");
  223. }
  224. }
  225. }
  226. function setUserPrivGuid() {
  227. $privGuid = create_guid();
  228. //($name, $value, $nosession=0)
  229. $this->setPreference('userPrivGuid', $privGuid, 0, 'global', $this);
  230. }
  231. /**
  232. * Interface for the User object to calling the UserPreference::setPreference() method in modules/UserPreferences/UserPreference.php
  233. *
  234. * @see UserPreference::setPreference()
  235. *
  236. * @param string $name Name of the preference to set
  237. * @param string $value Value to set preference to
  238. * @param null $nosession For BC, ignored
  239. * @param string $category Name of the category to retrieve
  240. */
  241. public function setPreference(
  242. $name,
  243. $value,
  244. $nosession = 0,
  245. $category = 'global'
  246. )
  247. {
  248. // for BC
  249. if ( func_num_args() > 4 ) {
  250. $user = func_get_arg(4);
  251. $GLOBALS['log']->deprecated('User::setPreferences() should not be used statically.');
  252. }
  253. else
  254. $user = $this;
  255. $user->_userPreferenceFocus->setPreference($name, $value, $category);
  256. }
  257. /**
  258. * Interface for the User object to calling the UserPreference::resetPreferences() method in modules/UserPreferences/UserPreference.php
  259. *
  260. * @see UserPreference::resetPreferences()
  261. *
  262. * @param string $category category to reset
  263. */
  264. public function resetPreferences(
  265. $category = null
  266. )
  267. {
  268. // for BC
  269. if ( func_num_args() > 1 ) {
  270. $user = func_get_arg(1);
  271. $GLOBALS['log']->deprecated('User::resetPreferences() should not be used statically.');
  272. }
  273. else
  274. $user = $this;
  275. $user->_userPreferenceFocus->resetPreferences($category);
  276. }
  277. /**
  278. * Interface for the User object to calling the UserPreference::savePreferencesToDB() method in modules/UserPreferences/UserPreference.php
  279. *
  280. * @see UserPreference::savePreferencesToDB()
  281. */
  282. public function savePreferencesToDB()
  283. {
  284. // for BC
  285. if ( func_num_args() > 0 ) {
  286. $user = func_get_arg(0);
  287. $GLOBALS['log']->deprecated('User::savePreferencesToDB() should not be used statically.');
  288. }
  289. else
  290. $user = $this;
  291. $user->_userPreferenceFocus->savePreferencesToDB();
  292. }
  293. /**
  294. * Unconditionally reloads user preferences from the DB and updates the session
  295. * @param string $category name of the category to retreive, defaults to global scope
  296. * @return bool successful?
  297. */
  298. public function reloadPreferences($category = 'global')
  299. {
  300. return $this->_userPreferenceFocus->reloadPreferences($category = 'global');
  301. }
  302. /**
  303. * Interface for the User object to calling the UserPreference::getUserDateTimePreferences() method in modules/UserPreferences/UserPreference.php
  304. *
  305. * @see UserPreference::getUserDateTimePreferences()
  306. *
  307. * @return array 'date' - date format for user ; 'time' - time format for user
  308. */
  309. public function getUserDateTimePreferences()
  310. {
  311. // for BC
  312. if ( func_num_args() > 0 ) {
  313. $user = func_get_arg(0);
  314. $GLOBALS['log']->deprecated('User::getUserDateTimePreferences() should not be used statically.');
  315. }
  316. else
  317. $user = $this;
  318. return $user->_userPreferenceFocus->getUserDateTimePreferences();
  319. }
  320. /**
  321. * Interface for the User object to calling the UserPreference::loadPreferences() method in modules/UserPreferences/UserPreference.php
  322. *
  323. * @see UserPreference::loadPreferences()
  324. *
  325. * @param string $category name of the category to retreive, defaults to global scope
  326. * @return bool successful?
  327. */
  328. public function loadPreferences(
  329. $category = 'global'
  330. )
  331. {
  332. // for BC
  333. if ( func_num_args() > 1 ) {
  334. $user = func_get_arg(1);
  335. $GLOBALS['log']->deprecated('User::loadPreferences() should not be used statically.');
  336. }
  337. else
  338. $user = $this;
  339. return $user->_userPreferenceFocus->loadPreferences($category);
  340. }
  341. /**
  342. * Interface for the User object to calling the UserPreference::setPreference() method in modules/UserPreferences/UserPreference.php
  343. *
  344. * @see UserPreference::getPreference()
  345. *
  346. * @param string $name name of the preference to retreive
  347. * @param string $category name of the category to retreive, defaults to global scope
  348. * @return mixed the value of the preference (string, array, int etc)
  349. */
  350. public function getPreference(
  351. $name,
  352. $category = 'global'
  353. )
  354. {
  355. // for BC
  356. if ( func_num_args() > 2 ) {
  357. $user = func_get_arg(2);
  358. $GLOBALS['log']->deprecated('User::getPreference() should not be used statically.');
  359. }
  360. else
  361. $user = $this;
  362. return $user->_userPreferenceFocus->getPreference($name, $category);
  363. }
  364. /**
  365. * incrementETag
  366. *
  367. * This function increments any ETag seed needed for a particular user's
  368. * UI. For example, if the user changes their theme, the ETag seed for the
  369. * main menu needs to be updated, so you call this function with the seed name
  370. * to do so:
  371. *
  372. * UserPreference::incrementETag("mainMenuETag");
  373. *
  374. * @param string $tag ETag seed name.
  375. * @return nothing
  376. */
  377. public function incrementETag($tag){
  378. $val = $this->getETagSeed($tag);
  379. if($val == 2147483648){
  380. $val = 0;
  381. }
  382. $val++;
  383. $this->setPreference($tag, $val, 0, "ETag");
  384. }
  385. /**
  386. * getETagSeed
  387. *
  388. * This function is a wrapper to encapsulate getting the ETag seed and
  389. * making sure it's sanitized for use in the app.
  390. *
  391. * @param string $tag ETag seed name.
  392. * @return integer numeric value of the seed
  393. */
  394. public function getETagSeed($tag){
  395. $val = $this->getPreference($tag, "ETag");
  396. if($val == null){
  397. $val = 0;
  398. }
  399. return $val;
  400. }
  401. /**
  402. * Get WHERE clause that fetches all users counted for licensing purposes
  403. * @return string
  404. */
  405. public static function getLicensedUsersWhere()
  406. {
  407. return "deleted=0 AND status='Active' AND user_name IS NOT NULL AND is_group=0 AND portal_only=0 AND ".$GLOBALS['db']->convert('user_name', 'length').">0";
  408. return "1<>1";
  409. }
  410. function save($check_notify = false) {
  411. $isUpdate = !empty($this->id) && !$this->new_with_id;
  412. $query = "SELECT count(id) as total from users WHERE ".self::getLicensedUsersWhere();
  413. // wp: do not save user_preferences in this table, see user_preferences module
  414. $this->user_preferences = '';
  415. // if this is an admin user, do not allow is_group or portal_only flag to be set.
  416. if ($this->is_admin) {
  417. $this->is_group = 0;
  418. $this->portal_only = 0;
  419. }
  420. parent::save($check_notify);
  421. $this->savePreferencesToDB();
  422. return $this->id;
  423. }
  424. /**
  425. * @return boolean true if the user is a member of the role_name, false otherwise
  426. * @param string $role_name - Must be the exact name of the acl_role
  427. * @param string $user_id - The user id to check for the role membership, empty string if current user
  428. * @desc Determine whether or not a user is a member of an ACL Role. This function caches the
  429. * results in the session or to prevent running queries after the first time executed.
  430. * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc..
  431. * All Rights Reserved..
  432. * Contributor(s): ______________________________________..
  433. */
  434. function check_role_membership($role_name, $user_id = ''){
  435. global $current_user;
  436. if(empty($user_id))
  437. $user_id = $current_user->id;
  438. // Check the Sugar External Cache to see if this users memberships were cached
  439. $role_array = sugar_cache_retrieve("RoleMemberships_".$user_id);
  440. // If we are pulling the roles for the current user
  441. if($user_id == $current_user->id){
  442. // If the Session doesn't contain the values
  443. if(!isset($_SESSION['role_memberships'])){
  444. // This means the external cache already had it loaded
  445. if(!empty($role_array))
  446. $_SESSION['role_memberships'] = $role_array;
  447. else{
  448. $_SESSION['role_memberships'] = ACLRole::getUserRoleNames($user_id);
  449. $role_array = $_SESSION['role_memberships'];
  450. }
  451. }
  452. // else the session had the values, so we assign to the role array
  453. else{
  454. $role_array = $_SESSION['role_memberships'];
  455. }
  456. }
  457. else{
  458. // If the external cache didn't contain the values, we get them and put them in cache
  459. if(!$role_array){
  460. $role_array = ACLRole::getUserRoleNames($user_id);
  461. sugar_cache_put("RoleMemberships_".$user_id, $role_array);
  462. }
  463. }
  464. // If the role doesn't exist in the list of the user's roles
  465. if(!empty($role_array) && in_array($role_name, $role_array))
  466. return true;
  467. else
  468. return false;
  469. }
  470. function get_summary_text() {
  471. //$this->_create_proper_name_field();
  472. return $this->name;
  473. }
  474. /**
  475. * @deprecated
  476. * @param string $user_name - Must be non null and at least 2 characters
  477. * @param string $user_password - Must be non null and at least 1 character.
  478. * @desc Take an unencrypted username and password and return the encrypted password
  479. * @return string encrypted password for storage in DB and comparison against DB password.
  480. */
  481. function encrypt_password($user_password)
  482. {
  483. // encrypt the password.
  484. $salt = substr($this->user_name, 0, 2);
  485. $encrypted_password = crypt($user_password, $salt);
  486. return $encrypted_password;
  487. }
  488. /**
  489. * Authenicates the user; returns true if successful
  490. *
  491. * @param string $password MD5-encoded password
  492. * @return bool
  493. */
  494. public function authenticate_user($password)
  495. {
  496. $row = self::findUserPassword($this->user_name, $password);
  497. if(empty($row)) {
  498. return false;
  499. } else {
  500. $this->id = $row['id'];
  501. return true;
  502. }
  503. }
  504. /**
  505. * retrieves an User bean
  506. * preformat name & full_name attribute with first/last
  507. * loads User's preferences
  508. *
  509. * @param string id ID of the User
  510. * @param bool encode encode the result
  511. * @return object User bean
  512. * @return null null if no User found
  513. */
  514. function retrieve($id, $encode = true, $deleted = true) {
  515. $ret = parent::retrieve($id, $encode, $deleted);
  516. if ($ret) {
  517. if (isset ($_SESSION)) {
  518. $this->loadPreferences();
  519. }
  520. }
  521. return $ret;
  522. }
  523. function retrieve_by_email_address($email) {
  524. $email1= strtoupper($email);
  525. $q=<<<EOQ
  526. select id from users where id in ( SELECT er.bean_id AS id FROM email_addr_bean_rel er,
  527. email_addresses ea WHERE ea.id = er.email_address_id
  528. AND ea.deleted = 0 AND er.deleted = 0 AND er.bean_module = 'Users' AND email_address_caps IN ('{$email1}') )
  529. EOQ;
  530. $res=$this->db->query($q);
  531. $row=$this->db->fetchByAssoc($res);
  532. if (!empty($row['id'])) {
  533. return $this->retrieve($row['id']);
  534. }
  535. return '';
  536. }
  537. function bean_implements($interface) {
  538. switch($interface){
  539. case 'ACL':return true;
  540. }
  541. return false;
  542. }
  543. /**
  544. * Load a user based on the user_name in $this
  545. * @param string $user_password Password
  546. * @param bool $password_encoded Is password md5-encoded or plain text?
  547. * @return -- this if load was successul and null if load failed.
  548. */
  549. function load_user($user_password, $password_encoded = false)
  550. {
  551. global $login_error;
  552. unset($GLOBALS['login_error']);
  553. if(isset ($_SESSION['loginattempts'])) {
  554. $_SESSION['loginattempts'] += 1;
  555. } else {
  556. $_SESSION['loginattempts'] = 1;
  557. }
  558. if($_SESSION['loginattempts'] > 5) {
  559. $GLOBALS['log']->fatal('SECURITY: '.$this->user_name.' has attempted to login '.$_SESSION['loginattempts'].' times from IP address: '.$_SERVER['REMOTE_ADDR'].'.');
  560. return null;
  561. }
  562. $GLOBALS['log']->debug("Starting user load for $this->user_name");
  563. if (!isset ($this->user_name) || $this->user_name == "" || !isset ($user_password) || $user_password == "")
  564. return null;
  565. if(!$password_encoded) {
  566. $user_password = md5($user_password);
  567. }
  568. $row = self::findUserPassword($this->user_name, $user_password);
  569. if(empty($row) || !empty ($GLOBALS['login_error'])) {
  570. $GLOBALS['log']->fatal('SECURITY: User authentication for '.$this->user_name.' failed - could not Load User from Database');
  571. return null;
  572. }
  573. // now fill in the fields.
  574. $this->loadFromRow($row);
  575. $this->loadPreferences();
  576. require_once ('modules/Versions/CheckVersions.php');
  577. $invalid_versions = get_invalid_versions();
  578. if (!empty ($invalid_versions)) {
  579. if (isset ($invalid_versions['Rebuild Relationships'])) {
  580. unset ($invalid_versions['Rebuild Relationships']);
  581. // flag for pickup in DisplayWarnings.php
  582. $_SESSION['rebuild_relationships'] = true;
  583. }
  584. if (isset ($invalid_versions['Rebuild Extensions'])) {
  585. unset ($invalid_versions['Rebuild Extensions']);
  586. // flag for pickup in DisplayWarnings.php
  587. $_SESSION['rebuild_extensions'] = true;
  588. }
  589. $_SESSION['invalid_versions'] = $invalid_versions;
  590. }
  591. if ($this->status != "Inactive")
  592. $this->authenticated = true;
  593. unset ($_SESSION['loginattempts']);
  594. return $this;
  595. }
  596. /**
  597. * Generate a new hash from plaintext password
  598. * @param string $password
  599. */
  600. public static function getPasswordHash($password)
  601. {
  602. if(!defined('CRYPT_MD5') || !constant('CRYPT_MD5')) {
  603. // does not support MD5 crypt - leave as is
  604. if(defined('CRYPT_EXT_DES') && constant('CRYPT_EXT_DES')) {
  605. return crypt(strtolower(md5($password)),
  606. "_.012".substr(str_shuffle('./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'), -4));
  607. }
  608. // plain crypt cuts password to 8 chars, which is not enough
  609. // fall back to old md5
  610. return strtolower(md5($password));
  611. }
  612. return crypt(strtolower(md5($password)));
  613. }
  614. /**
  615. * Check that password matches existing hash
  616. * @param string $password Plaintext password
  617. * @param string $user_hash DB hash
  618. */
  619. public static function checkPassword($password, $user_hash)
  620. {
  621. return self::checkPasswordMD5(md5($password), $user_hash);
  622. }
  623. /**
  624. * Check that md5-encoded password matches existing hash
  625. * @param string $password MD5-encoded password
  626. * @param string $user_hash DB hash
  627. * @return bool Match or not?
  628. */
  629. public static function checkPasswordMD5($password_md5, $user_hash)
  630. {
  631. if(empty($user_hash)) return false;
  632. if($user_hash[0] != '$' && strlen($user_hash) == 32) {
  633. // Old way - just md5 password
  634. return strtolower($password_md5) == $user_hash;
  635. }
  636. return crypt(strtolower($password_md5), $user_hash) == $user_hash;
  637. }
  638. /**
  639. * Find user with matching password
  640. * @param string $name Username
  641. * @param string $password MD5-encoded password
  642. * @param string $where Limiting query
  643. * @return the matching User of false if not found
  644. */
  645. public static function findUserPassword($name, $password, $where = '')
  646. {
  647. global $db;
  648. $name = $db->quote($name);
  649. $query = "SELECT * from users where user_name='$name'";
  650. if(!empty($where)) {
  651. $query .= " AND $where";
  652. }
  653. $result = $db->limitQuery($query,0,1,false);
  654. if(!empty($result)) {
  655. $row = $db->fetchByAssoc($result);
  656. if(self::checkPasswordMD5($password, $row['user_hash'])) {
  657. return $row;
  658. }
  659. }
  660. return false;
  661. }
  662. /**
  663. * Sets new password and resets password expiration timers
  664. * @param string $new_password
  665. */
  666. public function setNewPassword($new_password, $system_generated = '0')
  667. {
  668. $user_hash = self::getPasswordHash($new_password);
  669. $this->setPreference('loginexpiration','0');
  670. $this->setPreference('lockout','');
  671. $this->setPreference('loginfailed','0');
  672. $this->savePreferencesToDB();
  673. //set new password
  674. $now = TimeDate::getInstance()->nowDb();
  675. $query = "UPDATE $this->table_name SET user_hash='$user_hash', system_generated_password='$system_generated', pwd_last_changed='$now' where id='$this->id'";
  676. $this->db->query($query, true, "Error setting new password for $this->user_name: ");
  677. $_SESSION['hasExpiredPassword'] = '0';
  678. }
  679. /**
  680. * Verify that the current password is correct and write the new password to the DB.
  681. *
  682. * @param string $user name - Must be non null and at least 1 character.
  683. * @param string $user_password - Must be non null and at least 1 character.
  684. * @param string $new_password - Must be non null and at least 1 character.
  685. * @return boolean - If passwords pass verification and query succeeds, return true, else return false.
  686. */
  687. function change_password($user_password, $new_password, $system_generated = '0')
  688. {
  689. global $mod_strings;
  690. global $current_user;
  691. $GLOBALS['log']->debug("Starting password change for $this->user_name");
  692. if (!isset ($new_password) || $new_password == "") {
  693. $this->error_string = $mod_strings['ERR_PASSWORD_CHANGE_FAILED_1'].$current_user->user_name.$mod_strings['ERR_PASSWORD_CHANGE_FAILED_2'];
  694. return false;
  695. }
  696. // Check new password against rules set by admin
  697. if (!$this->check_password_rules($new_password)) {
  698. $this->error_string = $mod_strings['ERR_PASSWORD_CHANGE_FAILED_1'].$current_user->user_name.$mod_strings['ERR_PASSWORD_CHANGE_FAILED_3'];
  699. return false;
  700. }
  701. if (!$current_user->isAdminForModule('Users')) {
  702. //check old password first
  703. $row = self::findUserPassword($this->user_name, md5($user_password));
  704. if (empty($row)) {
  705. $GLOBALS['log']->warn("Incorrect old password for ".$this->user_name."");
  706. $this->error_string = $mod_strings['ERR_PASSWORD_INCORRECT_OLD_1'].$this->user_name.$mod_strings['ERR_PASSWORD_INCORRECT_OLD_2'];
  707. return false;
  708. }
  709. }
  710. $this->setNewPassword($new_password, $system_generated);
  711. return true;
  712. }
  713. /**
  714. * Check new password against rules set by admin
  715. * @param string $password
  716. * @return boolean
  717. */
  718. function check_password_rules($password) {
  719. $length = mb_strlen($password);
  720. // Min length
  721. if(!empty($GLOBALS["sugar_config"]["passwordsetting"]["minpwdlength"]) && $GLOBALS["sugar_config"]["passwordsetting"]["minpwdlength"] > 0 && $length < $GLOBALS["sugar_config"]["passwordsetting"]["minpwdlength"]) {
  722. return false;
  723. }
  724. // Max length
  725. if(!empty($GLOBALS['sugar_config']['passwordsetting']['maxpwdlength']) && $GLOBALS['sugar_config']['passwordsetting']['maxpwdlength'] > 0 && $length > $GLOBALS['sugar_config']['passwordsetting']['maxpwdlength']) {
  726. return false;
  727. }
  728. // One lower case
  729. if(!empty($GLOBALS["sugar_config"]["passwordsetting"]["onelower"]) && !preg_match('/[a-z]+/', $password)){
  730. return false;
  731. }
  732. // One upper case
  733. if(!empty($GLOBALS["sugar_config"]["passwordsetting"]["oneupper"]) && !preg_match('/[A-Z]+/', $password)){
  734. return false;
  735. }
  736. // One number
  737. if(!empty($GLOBALS["sugar_config"]["passwordsetting"]["onenumber"]) && !preg_match('/[0-9]+/', $password)){
  738. return false;
  739. }
  740. // One special character
  741. if(!empty($GLOBALS["sugar_config"]["passwordsetting"]["onespecial"]) && !preg_match('/[|}{~!@#$%^&*()_+=-]+/', $password)){
  742. return false;
  743. }
  744. // Custom regex
  745. if(!empty($GLOBALS["sugar_config"]["passwordsetting"]["customregex"]) && !preg_match($GLOBALS["sugar_config"]["passwordsetting"]["customregex"], $password)){
  746. return false;
  747. }
  748. return true;
  749. }
  750. function is_authenticated() {
  751. return $this->authenticated;
  752. }
  753. function fill_in_additional_list_fields() {
  754. $this->fill_in_additional_detail_fields();
  755. }
  756. function fill_in_additional_detail_fields() {
  757. global $locale;
  758. $query = "SELECT u1.first_name, u1.last_name from users u1, users u2 where u1.id = u2.reports_to_id AND u2.id = '$this->id' and u1.deleted=0";
  759. $result = $this->db->query($query, true, "Error filling in additional detail fields");
  760. $row = $this->db->fetchByAssoc($result);
  761. if ($row != null) {
  762. $this->reports_to_name = stripslashes($row['first_name'].' '.$row['last_name']);
  763. } else {
  764. $this->reports_to_name = '';
  765. }
  766. $this->_create_proper_name_field();
  767. }
  768. public function retrieve_user_id(
  769. $user_name
  770. )
  771. {
  772. $userFocus = new User;
  773. $userFocus->retrieve_by_string_fields(array('user_name'=>$user_name));
  774. if ( empty($userFocus->id) )
  775. return false;
  776. return $userFocus->id;
  777. }
  778. /**
  779. * @return -- returns a list of all users in the system.
  780. * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc..
  781. * All Rights Reserved..
  782. * Contributor(s): ______________________________________..
  783. */
  784. function verify_data($ieVerified=true) {
  785. global $mod_strings, $current_user;
  786. $verified = TRUE;
  787. if (!empty ($this->id)) {
  788. // Make sure the user doesn't report to themselves.
  789. $reports_to_self = 0;
  790. $check_user = $this->reports_to_id;
  791. $already_seen_list = array ();
  792. while (!empty ($check_user)) {
  793. if (isset ($already_seen_list[$check_user])) {
  794. // This user doesn't actually report to themselves
  795. // But someone above them does.
  796. $reports_to_self = 1;
  797. break;
  798. }
  799. if ($check_user == $this->id) {
  800. $reports_to_self = 1;
  801. break;
  802. }
  803. $already_seen_list[$check_user] = 1;
  804. $query = "SELECT reports_to_id FROM users WHERE id='".$this->db->quote($check_user)."'";
  805. $result = $this->db->query($query, true, "Error checking for reporting-loop");
  806. $row = $this->db->fetchByAssoc($result);
  807. echo ("fetched: ".$row['reports_to_id']." from ".$check_user."<br>");
  808. $check_user = $row['reports_to_id'];
  809. }
  810. if ($reports_to_self == 1) {
  811. $this->error_string .= $mod_strings['ERR_REPORT_LOOP'];
  812. $verified = FALSE;
  813. }
  814. }
  815. $query = "SELECT user_name from users where user_name='$this->user_name' AND deleted=0";
  816. if(!empty($this->id))$query .= " AND id<>'$this->id'";
  817. $result = $this->db->query($query, true, "Error selecting possible duplicate users: ");
  818. $dup_users = $this->db->fetchByAssoc($result);
  819. if (!empty($dup_users)) {
  820. $this->error_string .= $mod_strings['ERR_USER_NAME_EXISTS_1'].$this->user_name.$mod_strings['ERR_USER_NAME_EXISTS_2'];
  821. $verified = FALSE;
  822. }
  823. if (is_admin($current_user)) {
  824. $remaining_admins = $this->db->getOne("SELECT COUNT(*) as c from users where is_admin = 1 AND deleted=0");
  825. if (($remaining_admins <= 1) && ($this->is_admin != '1') && ($this->id == $current_user->id)) {
  826. $GLOBALS['log']->debug("Number of remaining administrator accounts: {$remaining_admins}");
  827. $this->error_string .= $mod_strings['ERR_LAST_ADMIN_1'].$this->user_name.$mod_strings['ERR_LAST_ADMIN_2'];
  828. $verified = FALSE;
  829. }
  830. }
  831. ///////////////////////////////////////////////////////////////////////
  832. //// InboundEmail verification failure
  833. if(!$ieVerified) {
  834. $verified = false;
  835. $this->error_string .= '<br />'.$mod_strings['ERR_EMAIL_NO_OPTS'];
  836. }
  837. return $verified;
  838. }
  839. function get_list_view_data() {
  840. global $current_user, $mod_strings;
  841. // Bug #48555 Not User Name Format of User's locale.
  842. $this->_create_proper_name_field();
  843. $user_fields = $this->get_list_view_array();
  844. if ($this->is_admin)
  845. $user_fields['IS_ADMIN_IMAGE'] = SugarThemeRegistry::current()->getImage('check_inline', '',null,null,'.gif',$mod_strings['LBL_CHECKMARK']);
  846. elseif (!$this->is_admin) $user_fields['IS_ADMIN'] = '';
  847. if ($this->is_group)
  848. $user_fields['IS_GROUP_IMAGE'] = SugarThemeRegistry::current()->getImage('check_inline', '',null,null,'.gif',$mod_strings['LBL_CHECKMARK']);
  849. else
  850. $user_fields['IS_GROUP_IMAGE'] = '';
  851. if ($this->is_admin) {
  852. $user_fields['IS_ADMIN_IMAGE'] = SugarThemeRegistry::current()->getImage('check_inline', '',null,null,'.gif',translate('LBL_CHECKMARK', 'Users'));
  853. } elseif (!$this->is_admin) {
  854. $user_fields['IS_ADMIN'] = '';
  855. }
  856. if ($this->is_group) {
  857. $user_fields['IS_GROUP_IMAGE'] = SugarThemeRegistry::current()->getImage('check_inline', '',null,null,'.gif',translate('LBL_CHECKMARK', 'Users'));
  858. } else {
  859. $user_fields['NAME'] = empty ($this->name) ? '' : $this->name;
  860. }
  861. $user_fields['REPORTS_TO_NAME'] = $this->reports_to_name;
  862. $user_fields['EMAIL1'] = $this->emailAddress->getPrimaryAddress($this);
  863. return $user_fields;
  864. }
  865. function list_view_parse_additional_sections(& $list_form, $xTemplateSection) {
  866. return $list_form;
  867. }
  868. /**
  869. * getAllUsers
  870. *
  871. * Returns all active and inactive users
  872. * @return Array of all users in the system
  873. */
  874. public static function getAllUsers()
  875. {
  876. $active_users = get_user_array(FALSE);
  877. $inactive_users = get_user_array(FALSE, "Inactive");
  878. $result = $active_users + $inactive_users;
  879. asort($result);
  880. return $result;
  881. }
  882. function create_export_query($order_by, $where) {
  883. include('modules/Users/field_arrays.php');
  884. $cols = '';
  885. foreach($fields_array['User']['export_fields'] as $field) {
  886. $cols .= (empty($cols)) ? '' : ', ';
  887. $cols .= $field;
  888. }
  889. $query = "SELECT {$cols} FROM users ";
  890. $where_auto = " users.deleted = 0";
  891. if ($where != "")
  892. $query .= " WHERE $where AND ".$where_auto;
  893. else
  894. $query .= " WHERE ".$where_auto;
  895. // admin for module user is not be able to export a super-admin
  896. global $current_user;
  897. if(!$current_user->is_admin){
  898. $query .= " AND users.is_admin=0";
  899. }
  900. if ($order_by != "")
  901. $query .= " ORDER BY $order_by";
  902. else
  903. $query .= " ORDER BY users.user_name";
  904. return $query;
  905. }
  906. /** Returns a list of the associated users
  907. * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc..
  908. * All Rights Reserved..
  909. * Contributor(s): ______________________________________..
  910. */
  911. function get_meetings() {
  912. // First, get the list of IDs.
  913. $query = "SELECT meeting_id as id from meetings_users where user_id='$this->id' AND deleted=0";
  914. return $this->build_related_list($query, new Meeting());
  915. }
  916. function get_calls() {
  917. // First, get the list of IDs.
  918. $query = "SELECT call_id as id from calls_users where user_id='$this->id' AND deleted=0";
  919. return $this->build_related_list($query, new Call());
  920. }
  921. /**
  922. * generates Javascript to display I-E mail counts, both personal and group
  923. */
  924. function displayEmailCounts() {
  925. global $theme;
  926. $new = translate('LBL_NEW', 'Emails');
  927. $default = 'index.php?module=Emails&action=ListView&assigned_user_id='.$this->id;
  928. $count = '';
  929. $verts = array('Love', 'Links', 'Pipeline', 'RipCurl', 'SugarLite');
  930. if($this->hasPersonalEmail()) {
  931. $r = $this->db->query('SELECT count(*) AS c FROM emails WHERE deleted=0 AND assigned_user_id = \''.$this->id.'\' AND type = \'inbound\' AND status = \'unread\'');
  932. $a = $this->db->fetchByAssoc($r);
  933. if(in_array($theme, $verts)) {
  934. $count .= '<br />';
  935. } else {
  936. $count .= '&nbsp;&nbsp;&nbsp;&nbsp;';
  937. }
  938. $count .= '<a href='.$default.'&type=inbound>'.translate('LBL_LIST_TITLE_MY_INBOX', 'Emails').': ('.$a['c'].' '.$new.')</a>';
  939. if(!in_array($theme, $verts)) {
  940. $count .= ' - ';
  941. }
  942. }
  943. $r = $this->db->query('SELECT id FROM users WHERE users.is_group = 1 AND deleted = 0');
  944. $groupIds = '';
  945. $groupNew = '';
  946. while($a = $this->db->fetchByAssoc($r)) {
  947. if($groupIds != '') {$groupIds .= ', ';}
  948. $groupIds .= "'".$a['id']."'";
  949. }
  950. $total = 0;
  951. if(strlen($groupIds) > 0) {
  952. $groupQuery = 'SELECT count(*) AS c FROM emails ';
  953. $groupQuery .= ' WHERE emails.deleted=0 AND emails.assigned_user_id IN ('.$groupIds.') AND emails.type = \'inbound\' AND emails.status = \'unread\'';
  954. $r = $this->db->query($groupQuery);
  955. if(is_resource($r)) {
  956. $a = $this->db->fetchByAssoc($r);
  957. if($a['c'] > 0) {
  958. $total = $a['c'];
  959. }
  960. }
  961. }
  962. if(in_array($theme, $verts)) $count .= '<br />';
  963. if(empty($count)) $count .= '&nbsp;&nbsp;&nbsp;&nbsp;';
  964. $count .= '<a href=index.php?module=Emails&action=ListViewGroup>'.translate('LBL_LIST_TITLE_GROUP_INBOX', 'Emails').': ('.$total.' '.$new.')</a>';
  965. $out = '<script type="text/javascript" language="Javascript">';
  966. $out .= 'var welcome = document.getElementById("welcome");';
  967. $out .= 'var welcomeContent = welcome.innerHTML;';
  968. $out .= 'welcome.innerHTML = welcomeContent + "'.$count.'";';
  969. $out .= '</script>';
  970. echo $out;
  971. }
  972. function getPreferredEmail() {
  973. $ret = array ();
  974. $nameEmail = $this->getUsersNameAndEmail();
  975. $prefAddr = $nameEmail['email'];
  976. $fullName = $nameEmail['name'];
  977. if (empty ($prefAddr)) {
  978. $nameEmail = $this->getSystemDefaultNameAndEmail();
  979. $prefAddr = $nameEmail['email'];
  980. $fullName = $nameEmail['name'];
  981. } // if
  982. $fullName = from_html($fullName);
  983. $ret['name'] = $fullName;
  984. $ret['email'] = $prefAddr;
  985. return $ret;
  986. }
  987. function getUsersNameAndEmail()
  988. {
  989. // Bug #48555 Not User Name Format of User's locale.
  990. $this->_create_proper_name_field();
  991. $prefAddr = $this->emailAddress->getPrimaryAddress($this);
  992. if (empty ($prefAddr)) {
  993. $prefAddr = $this->emailAddress->getReplyToAddress($this);
  994. }
  995. return array('email' => $prefAddr , 'name' => $this->name);
  996. } // fn
  997. function getSystemDefaultNameAndEmail() {
  998. $email = new Email();
  999. $return = $email->getSystemDefaultEmail();
  1000. $prefAddr = $return['email'];
  1001. $fullName = $return['name'];
  1002. return array('email' => $prefAddr , 'name' => $fullName);
  1003. } // fn
  1004. /**
  1005. * sets User email default in config.php if not already set by install - i.
  1006. * e., upgrades
  1007. */
  1008. function setDefaultsInConfig() {
  1009. global $sugar_config;
  1010. $sugar_config['email_default_client'] = 'sugar';
  1011. $sugar_config['email_default_editor'] = 'html';
  1012. ksort($sugar_config);
  1013. write_array_to_file('sugar_config', $sugar_config, 'config.php');
  1014. return $sugar_config;
  1015. }
  1016. /**
  1017. * returns User's email address based on descending order of preferences
  1018. *
  1019. * @param string id GUID of target user if needed
  1020. * @return array Assoc array for an email and name
  1021. */
  1022. function getEmailInfo($id='') {
  1023. $user = $this;
  1024. if(!empty($id)) {
  1025. $user = new User();
  1026. $user->retrieve($id);
  1027. }
  1028. // from name
  1029. $fromName = $user->getPreference('mail_fromname');
  1030. if(empty($fromName)) {
  1031. // cn: bug 8586 - localized name format
  1032. $fromName = $user->full_name;
  1033. }
  1034. // from address
  1035. $fromaddr = $user->getPreference('mail_fromaddress');
  1036. if(empty($fromaddr)) {
  1037. if(!empty($user->email1) && isset($user->email1)) {
  1038. $fromaddr = $user->email1;
  1039. } elseif(!empty($user->email2) && isset($user->email2)) {
  1040. $fromaddr = $user->email2;
  1041. } else {
  1042. $r = $user->db->query("SELECT value FROM config WHERE name = 'fromaddress'");
  1043. $a = $user->db->fetchByAssoc($r);
  1044. $fromddr = $a['value'];
  1045. }
  1046. }
  1047. $ret['name'] = $fromName;
  1048. $ret['email'] = $fromaddr;
  1049. return $ret;
  1050. }
  1051. /**
  1052. * returns opening <a href=xxxx for a contact, account, etc
  1053. * cascades from User set preference to System-wide default
  1054. * @return string link
  1055. * @param attribute the email addy
  1056. * @param focus the parent bean
  1057. * @param contact_id
  1058. * @param return_module
  1059. * @param return_action
  1060. * @param return_id
  1061. * @param class
  1062. */
  1063. function getEmailLink2($emailAddress, &$focus, $contact_id='', $ret_module='', $ret_action='DetailView', $ret_id='', $class='') {
  1064. $emailLink = '';
  1065. global $sugar_config;
  1066. if(!isset($sugar_config['email_default_client'])) {
  1067. $this->setDefaultsInConfig();
  1068. }
  1069. $userPref = $this->getPreference('email_link_type');
  1070. $defaultPref = $sugar_config['email_default_client'];
  1071. if($userPref != '') {
  1072. $client = $userPref;
  1073. } else {
  1074. $client = $defaultPref;
  1075. }
  1076. if($client == 'sugar') {
  1077. $email = '';
  1078. $to_addrs_ids = '';
  1079. $to_addrs_names = '';
  1080. $to_addrs_emails = '';
  1081. $fullName = !empty($focus->name) ? $focus->name : '';
  1082. if(empty($ret_module)) $ret_module = $focus->module_dir;
  1083. if(empty($ret_id)) $ret_id = $focus->id;
  1084. if($focus->object_name == 'Contact') {
  1085. $contact_id = $focus->id;
  1086. $to_addrs_ids = $focus->id;
  1087. // Bug #48555 Not User Name Format of User's locale.
  1088. $focus->_create_proper_name_field();
  1089. $fullName = $focus->name;
  1090. $to_addrs_names = $fullName;
  1091. $to_addrs_emails = $focus->email1;
  1092. }
  1093. $emailLinkUrl = 'contact_id='.$contact_id.
  1094. '&parent_type='.$focus->module_dir.
  1095. '&parent_id='.$focus->id.
  1096. '&parent_name='.urlencode($fullName).
  1097. '&to_addrs_ids='.$to_addrs_ids.
  1098. '&to_addrs_names='.urlencode($to_addrs_names).
  1099. '&to_addrs_emails='.urlencode($to_addrs_emails).
  1100. '&to_email_addrs='.urlencode($fullName . '&nbsp;&lt;' . $emailAddress . '&gt;').
  1101. '&return_module='.$ret_module.
  1102. '&return_action='.$ret_action.
  1103. '&return_id='.$ret_id;
  1104. //Generate the compose package for the quick create options.
  1105. //$json = getJSONobj();
  1106. //$composeOptionsLink = $json->encode( array('composeOptionsLink' => $emailLinkUrl,'id' => $focus->id) );
  1107. require_once('modules/Emails/EmailUI.php');
  1108. $eUi = new EmailUI();
  1109. $j_quickComposeOptions = $eUi->generateComposePackageForQuickCreateFromComposeUrl($emailLinkUrl, true);
  1110. $emailLink = "<a href='javascript:void(0);' onclick='SUGAR.quickCompose.init($j_quickComposeOptions);' class='$class'>";
  1111. } else {
  1112. // straight mailto:
  1113. $emailLink = '<a href="mailto:'.$emailAddress.'" class="'.$class.'">';
  1114. }
  1115. return $emailLink;
  1116. }
  1117. /**
  1118. * returns opening <a href=xxxx for a contact, account, etc
  1119. * cascades from User set preference to System-wide default
  1120. * @return string link
  1121. * @param attribute the email addy
  1122. * @param focus the parent bean
  1123. * @param contact_id
  1124. * @param return_module
  1125. * @param return_action
  1126. * @param return_id
  1127. * @param class
  1128. */
  1129. function getEmailLink($attribute, &$focus, $contact_id='', $ret_module='', $ret_action='DetailView', $ret_id='', $class='') {
  1130. $emailLink = '';
  1131. global $sugar_config;
  1132. if(!isset($sugar_config['email_default_client'])) {
  1133. $this->setDefaultsInConfig();
  1134. }
  1135. $userPref = $this->getPreference('email_link_type');
  1136. $defaultPref = $sugar_config['email_default_client'];
  1137. if($userPref != '') {
  1138. $client = $userPref;
  1139. } else {
  1140. $client = $defaultPref;
  1141. }
  1142. if($client == 'sugar') {
  1143. $email = '';
  1144. $to_addrs_ids = '';
  1145. $to_addrs_names = '';
  1146. $to_addrs_emails = '';
  1147. $fullName = !empty($focus->name) ? $focus->name : '';
  1148. if(!empty($focus->$attribute)) {
  1149. $email = $focus->$attribute;
  1150. }
  1151. if(empty($ret_module)) $ret_module = $focus->module_dir;
  1152. if(empty($ret_id)) $ret_id = $focus->id;
  1153. if($focus->object_name == 'Contact') {
  1154. // Bug #48555 Not User Name Format of User's locale.
  1155. $focus->_create_proper_name_field();
  1156. $fullName = $focus->name;
  1157. $contact_id = $focus->id;
  1158. $to_addrs_ids = $focus->id;
  1159. $to_addrs_names = $fullName;
  1160. $to_addrs_emails = $focus->email1;
  1161. }
  1162. $emailLinkUrl = 'contact_id='.$contact_id.
  1163. '&parent_type='.$focus->module_dir.
  1164. '&parent_id='.$focus->id.
  1165. '&parent_name='.urlencode($fullName).
  1166. '&to_addrs_ids='.$to_addrs_ids.
  1167. '&to_addrs_names='.urlencode($to_addrs_names).
  1168. '&to_addrs_emails='.urlencode($to_addrs_emails).
  1169. '&to_email_addrs='.urlencode($fullName . '&nbsp;&lt;' . $email . '&gt;').
  1170. '&return_module='.$ret_module.
  1171. '&return_action='.$ret_action.
  1172. '&return_id='.$ret_id;
  1173. //Generate the compose package for the quick create options.
  1174. require_once('modules/Emails/EmailUI.php');
  1175. $eUi = new EmailUI();
  1176. $j_quickComposeOptions = $eUi->generateComposePackageForQuickCreateFromComposeUrl($emailLinkUrl, true);
  1177. $emailLink = "<a href='javascript:void(0);' onclick='SUGAR.quickCompose.init($j_quickComposeOptions);' class='$class'>";
  1178. } else {
  1179. // straight mailto:
  1180. $emailLink = '<a href="mailto:'.$focus->$attribute.'" class="'.$class.'">';
  1181. }
  1182. return $emailLink;
  1183. }
  1184. /**
  1185. * gets a human-readable explanation of the format macro
  1186. * @return string Human readable name format
  1187. */
  1188. function getLocaleFormatDesc() {
  1189. global $locale;
  1190. global $mod_strings;
  1191. global $app_strings;
  1192. $format['f'] = $mod_strings['LBL_LOCALE_DESC_FIRST'];
  1193. $format['l'] = $mod_strings['LBL_LOCALE_DESC_LAST'];
  1194. $format['s'] = $mod_strings['LBL_LOCALE_DESC_SALUTATION'];
  1195. $format['t'] = $mod_strings['LBL_LOCALE_DESC_TITLE'];
  1196. $name['f'] = $app_strings['LBL_LOCALE_NAME_EXAMPLE_FIRST'];
  1197. $name['l'] = $app_strings['LBL_LOCALE_NAME_EXAMPLE_LAST'];
  1198. $name['s'] = $app_strings['LBL_LOCALE_NAME_EXAMPLE_SALUTATION'];
  1199. $name['t'] = $app_strings['LBL_LOCALE_NAME_EXAMPLE_TITLE'];
  1200. $macro = $locale->getLocaleFormatMacro();
  1201. $ret1 = '';
  1202. $ret2 = '';
  1203. for($i=0; $i<strlen($macro); $i++) {
  1204. if(array_key_exists($macro{$i}, $format)) {
  1205. $ret1 .= "<i>".$format[$macro{$i}]."</i>";
  1206. $ret2 .= "<i>".$name[$macro{$i}]."</i>";
  1207. } else {
  1208. $ret1 .= $macro{$i};
  1209. $ret2 .= $macro{$i};
  1210. }
  1211. }
  1212. return $ret1."<br />".$ret2;
  1213. }
  1214. /*
  1215. *
  1216. * Here are the multi level admin access check functions.
  1217. *
  1218. */
  1219. /**
  1220. * Helper function to remap some modules around ACL wise
  1221. *
  1222. * @return string
  1223. */
  1224. protected function _fixupModuleForACL($module) {
  1225. if($module=='ContractTypes') {
  1226. $module = 'Contracts';
  1227. }
  1228. if(preg_match('/Product[a-zA-Z]*/',$module)) {
  1229. $module = 'Products';
  1230. }
  1231. return $module;
  1232. }
  1233. /**
  1234. * Helper function that enumerates the list of modules and checks if they are an admin/dev.
  1235. * The code was just too similar to copy and paste.
  1236. *
  1237. * @return array
  1238. */
  1239. protected function _getModulesForACL($type='dev'){
  1240. $isDev = $type=='dev';
  1241. $isAdmin = $type=='admin';
  1242. global $beanList;
  1243. $myModules = array();
  1244. if (!is_array($beanList) ) {
  1245. return $myModules;
  1246. }
  1247. // These modules don't take kindly to the studio trying to play about with them.
  1248. static $ignoredModuleList = array('iFrames','Feeds','Home','Dashboard','Calendar','Activities','Reports');
  1249. $actions = ACLAction::getUserActions($this->id);
  1250. foreach ($beanList as $module=>$val) {
  1251. // Remap the module name
  1252. $module = $this->_fixupModuleForACL($module);
  1253. if (in_array($module,$myModules)) {
  1254. // Already have the module in the list
  1255. continue;
  1256. }
  1257. if (in_array($module,$ignoredModuleList)) {
  1258. // You can't develop on these modules.
  1259. continue;
  1260. }
  1261. $focus = SugarModule::get($module)->loadBean();
  1262. if ( $focus instanceOf SugarBean ) {
  1263. $key = $focus->acltype;
  1264. } else {
  1265. $key = 'module';
  1266. }
  1267. if (($this->isAdmin() && isset($actions[$module][$key]))
  1268. ) {
  1269. $myModules[] = $module;
  1270. }
  1271. }
  1272. return $myModules;
  1273. }
  1274. /**
  1275. * Is this user a system wide admin
  1276. *
  1277. * @return bool
  1278. */
  1279. public function isAdmin() {
  1280. if(isset($this->is_admin)
  1281. &&($this->is_admin == '1' || $this->is_admin === 'on')){
  1282. return true;
  1283. }
  1284. return false;
  1285. }
  1286. /**
  1287. * Is this user a developer for any module
  1288. *
  1289. * @return bool
  1290. */
  1291. public function isDeveloperForAnyModule() {
  1292. if ($this->isAdmin()) {
  1293. return true;
  1294. }
  1295. return false;
  1296. }
  1297. /**
  1298. * List the modules a user has developer access to
  1299. *
  1300. * @return array
  1301. */
  1302. public function getDeveloperModules() {
  1303. static $developerModules;
  1304. if (!isset($_SESSION[$this->user_name.'_get_developer_modules_for_user']) ) {
  1305. $_SESSION[$this->user_name.'_get_developer_modules_for_user'] = $this->_getModulesForACL('dev');
  1306. }
  1307. return $_SESSION[$this->user_name.'_get_developer_modules_for_user'];
  1308. }
  1309. /**
  1310. * Is this user a developer for the specified module
  1311. *
  1312. * @return bool
  1313. */
  1314. public function isDeveloperForModule($module) {
  1315. if ($this->isAdmin()) {
  1316. return true;
  1317. }
  1318. $devModules = $this->getDeveloperModules();
  1319. $module = $this->_fixupModuleForACL($module);
  1320. if (in_array($module,$devModules) ) {
  1321. return true;
  1322. }
  1323. return false;
  1324. }
  1325. /**
  1326. * List the modules a user has admin access to
  1327. *
  1328. * @return array
  1329. */
  1330. public function getAdminModules() {
  1331. if (!isset($_SESSION[$this->user_name.'_get_admin_modules_for_user']) ) {
  1332. $_SESSION[$this->user_name.'_get_admin_modules_for_user'] = $this->_getModulesForACL('admin');
  1333. }
  1334. return $_SESSION[$this->user_name.'_get_admin_modules_for_user'];
  1335. }
  1336. /**
  1337. * Is this user an admin for the specified module
  1338. *
  1339. * @return bool
  1340. */
  1341. public function isAdminForModule($module) {
  1342. if ($this->isAdmin()) {
  1343. return true;
  1344. }
  1345. $adminModules = $this->getAdminModules();
  1346. $module = $this->_fixupModuleForACL($module);
  1347. if (in_array($module,$adminModules) ) {
  1348. return true;
  1349. }
  1350. return false;
  1351. }
  1352. /**
  1353. * Whether or not based on the user's locale if we should show the last name first.
  1354. *
  1355. * @return bool
  1356. */
  1357. public function showLastNameFirst(){
  1358. global $locale;
  1359. $localeFormat = $locale->getLocaleFormatMacro($this);
  1360. if ( strpos($localeFormat,'l') > strpos($localeFormat,'f') ) {
  1361. return false;
  1362. }else {
  1363. return true;
  1364. }
  1365. }
  1366. function create_new_list_query($order_by, $where,$filter=array(),$params=array(), $show_deleted = 0,$join_type='', $return_array = false,$parentbean=null, $singleSelect = false)
  1367. { //call parent method, specifying for array to be returned
  1368. $ret_array = parent::create_new_list_query($order_by, $where,$filter,$params, $show_deleted,$join_type, true,$parentbean, $singleSelect);
  1369. //if this is being called from webservices, then run additional code
  1370. if(!empty($GLOBALS['soap_server_object'])){
  1371. //if this is a single select, then…

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