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.
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.
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
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
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.
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.
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 ********************************************************************************/
38 require_once('include/SugarObjects/templates/person/Person.php');
40 // User is used to store customer information.
41 class User extends Person {
53 var $modified_user_id;
56 var $modified_by_name;
68 var $address_postalcode;
74 var $authenticated = false;
81 var $accept_status; // to support Meetings
82 //adding a property called team_id so we can populate it for use in the team widget
85 var $receive_notifications;
89 var $team_exists = false;
90 var $table_name = "users";
91 var $module_dir = 'Users';
92 var $object_name = "User";
93 var $user_preferences;
95 var $importable = true;
96 var $_userPreferenceFocus;
98 var $encodeFields = Array ("first_name", "last_name", "description");
100 // This is used to retrieve related fields from form posts.
101 var $additional_column_fields = array ('reports_to_name'
107 var $new_schema = true;
112 $this->_loadUserPreferencesFocus();
115 protected function _loadUserPreferencesFocus()
117 $this->_userPreferenceFocus = new UserPreference($this);
121 * returns an admin user
123 public function getSystemUser()
125 if (null === $this->retrieve('1'))
126 // handle cases where someone deleted user with id "1"
127 $this->retrieve_by_string_fields(array(
128 'status' => 'Active',
137 * convenience function to get user's default signature
139 function getDefaultSignature() {
140 if($defaultId = $this->getPreference('signature_default')) {
141 return $this->getSignature($defaultId);
148 * retrieves the signatures for a user
149 * @param string id ID of user_signature
150 * @return array ID, signature, and signature_html
152 public function getSignature($id)
154 $signatures = $this->getSignaturesArray();
156 return isset($signatures[$id]) ? $signatures[$id] : FALSE;
159 function getSignaturesArray() {
160 $q = 'SELECT * FROM users_signatures WHERE user_id = \''.$this->id.'\' AND deleted = 0 ORDER BY name ASC';
161 $r = $this->db->query($q);
164 $sig = array(""=>"");
166 while($a = $this->db->fetchByAssoc($r)) {
174 * retrieves any signatures that the User may have created as <select>
176 public function getSignatures(
182 $sig = $this->getSignaturesArray();
184 foreach ($sig as $key => $arr)
186 $sigs[$key] = !empty($arr['name']) ? $arr['name'] : '';
191 $change = ($forSettings) ? "onChange='displaySignatureEdit();'" : "onChange='setSigEditButtonVisibility();'";
194 $id = (!$forSettings) ? 'signature_id' : 'signature_idDisplay';
196 $out = "<select {$change} id='{$id}' name='{$id}'>";
197 $out .= get_select_options_with_id($sigs, $defaultSig).'</select>';
203 * returns buttons and JS for signatures
205 function getSignatureButtons($jscall='', $defaultDisplay=false) {
208 $jscall = empty($jscall) ? 'open_email_signature_form' : $jscall;
210 $butts = "<input class='button' onclick='javascript:{$jscall}(\"\", \"{$this->id}\");' value='{$mod_strings['LBL_BUTTON_CREATE']}' type='button'> ";
211 if($defaultDisplay) {
212 $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">
215 $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">
222 * performs a rudimentary check to verify if a given user has setup personal
227 public function hasPersonalEmail()
229 $focus = new InboundEmail;
230 $focus->retrieve_by_string_fields(array('group_id' => $this->id));
232 return !empty($focus->id);
235 /* Returns the User's private GUID; this is unassociated with the User's
236 * actual GUID. It is used to secure file names that must be HTTP://
237 * accesible, but obfusicated.
239 function getUserPrivGuid() {
240 $userPrivGuid = $this->getPreference('userPrivGuid', 'global', $this);
242 return $userPrivGuid;
244 $this->setUserPrivGuid();
245 if (!isset ($_SESSION['setPrivGuid'])) {
246 $_SESSION['setPrivGuid'] = true;
247 $userPrivGuid = $this->getUserPrivGuid();
248 return $userPrivGuid;
250 sugar_die("Breaking Infinite Loop Condition: Could not setUserPrivGuid.");
255 function setUserPrivGuid() {
256 $privGuid = create_guid();
257 //($name, $value, $nosession=0)
258 $this->setPreference('userPrivGuid', $privGuid, 0, 'global', $this);
262 * Interface for the User object to calling the UserPreference::setPreference() method in modules/UserPreferences/UserPreference.php
264 * @see UserPreference::setPreference()
266 * @param string $name Name of the preference to set
267 * @param string $value Value to set preference to
268 * @param null $nosession For BC, ignored
269 * @param string $category Name of the category to retrieve
271 public function setPreference(
279 if ( func_num_args() > 4 ) {
280 $user = func_get_arg(4);
281 $GLOBALS['log']->deprecated('User::setPreferences() should not be used statically.');
286 $user->_userPreferenceFocus->setPreference($name, $value, $category);
290 * Interface for the User object to calling the UserPreference::resetPreferences() method in modules/UserPreferences/UserPreference.php
292 * @see UserPreference::resetPreferences()
294 * @param string $category category to reset
296 public function resetPreferences(
301 if ( func_num_args() > 1 ) {
302 $user = func_get_arg(1);
303 $GLOBALS['log']->deprecated('User::resetPreferences() should not be used statically.');
308 $user->_userPreferenceFocus->resetPreferences($category);
312 * Interface for the User object to calling the UserPreference::savePreferencesToDB() method in modules/UserPreferences/UserPreference.php
314 * @see UserPreference::savePreferencesToDB()
316 public function savePreferencesToDB()
319 if ( func_num_args() > 0 ) {
320 $user = func_get_arg(0);
321 $GLOBALS['log']->deprecated('User::savePreferencesToDB() should not be used statically.');
326 $user->_userPreferenceFocus->savePreferencesToDB();
330 * Unconditionally reloads user preferences from the DB and updates the session
331 * @param string $category name of the category to retreive, defaults to global scope
332 * @return bool successful?
334 public function reloadPreferences($category = 'global')
336 return $this->_userPreferenceFocus->reloadPreferences($category = 'global');
340 * Interface for the User object to calling the UserPreference::getUserDateTimePreferences() method in modules/UserPreferences/UserPreference.php
342 * @see UserPreference::getUserDateTimePreferences()
344 * @return array 'date' - date format for user ; 'time' - time format for user
346 public function getUserDateTimePreferences()
349 if ( func_num_args() > 0 ) {
350 $user = func_get_arg(0);
351 $GLOBALS['log']->deprecated('User::getUserDateTimePreferences() should not be used statically.');
356 return $user->_userPreferenceFocus->getUserDateTimePreferences();
360 * Interface for the User object to calling the UserPreference::loadPreferences() method in modules/UserPreferences/UserPreference.php
362 * @see UserPreference::loadPreferences()
364 * @param string $category name of the category to retreive, defaults to global scope
365 * @return bool successful?
367 public function loadPreferences(
372 if ( func_num_args() > 1 ) {
373 $user = func_get_arg(1);
374 $GLOBALS['log']->deprecated('User::loadPreferences() should not be used statically.');
379 return $user->_userPreferenceFocus->loadPreferences($category);
383 * Interface for the User object to calling the UserPreference::setPreference() method in modules/UserPreferences/UserPreference.php
385 * @see UserPreference::getPreference()
387 * @param string $name name of the preference to retreive
388 * @param string $category name of the category to retreive, defaults to global scope
389 * @return mixed the value of the preference (string, array, int etc)
391 public function getPreference(
397 if ( func_num_args() > 2 ) {
398 $user = func_get_arg(2);
399 $GLOBALS['log']->deprecated('User::getPreference() should not be used statically.');
404 return $user->_userPreferenceFocus->getPreference($name, $category);
410 * This function increments any ETag seed needed for a particular user's
411 * UI. For example, if the user changes their theme, the ETag seed for the
412 * main menu needs to be updated, so you call this function with the seed name
415 * UserPreference::incrementETag("mainMenuETag");
417 * @param string $tag ETag seed name.
420 public function incrementETag($tag){
421 $val = $this->getETagSeed($tag);
422 if($val == 2147483648){
426 $this->setPreference($tag, $val, 0, "ETag");
432 * This function is a wrapper to encapsulate getting the ETag seed and
433 * making sure it's sanitized for use in the app.
435 * @param string $tag ETag seed name.
436 * @return integer numeric value of the seed
438 public function getETagSeed($tag){
439 $val = $this->getPreference($tag, "ETag");
448 * Get WHERE clause that fetches all users counted for licensing purposes
451 public static function getLicensedUsersWhere()
453 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";
457 function save($check_notify = false) {
458 $isUpdate = !empty($this->id) && !$this->new_with_id;
461 $query = "SELECT count(id) as total from users WHERE ".self::getLicensedUsersWhere();
464 // wp: do not save user_preferences in this table, see user_preferences module
465 $this->user_preferences = '';
467 // if this is an admin user, do not allow is_group or portal_only flag to be set.
468 if ($this->is_admin) {
470 $this->portal_only = 0;
474 // set some default preferences when creating a new user
475 $setNewUserPreferences = empty($this->id) || !empty($this->new_with_id);
479 parent::save($check_notify);
482 // set some default preferences when creating a new user
483 if ( $setNewUserPreferences ) {
484 if(!$this->getPreference('calendar_publish_key')) {
485 $this->setPreference('calendar_publish_key', create_guid());
489 $this->savePreferencesToDB();
494 * @return boolean true if the user is a member of the role_name, false otherwise
495 * @param string $role_name - Must be the exact name of the acl_role
496 * @param string $user_id - The user id to check for the role membership, empty string if current user
497 * @desc Determine whether or not a user is a member of an ACL Role. This function caches the
498 * results in the session or to prevent running queries after the first time executed.
499 * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc..
500 * All Rights Reserved..
501 * Contributor(s): ______________________________________..
503 function check_role_membership($role_name, $user_id = ''){
505 global $current_user;
508 $user_id = $current_user->id;
510 // Check the Sugar External Cache to see if this users memberships were cached
511 $role_array = sugar_cache_retrieve("RoleMemberships_".$user_id);
513 // If we are pulling the roles for the current user
514 if($user_id == $current_user->id){
515 // If the Session doesn't contain the values
516 if(!isset($_SESSION['role_memberships'])){
517 // This means the external cache already had it loaded
518 if(!empty($role_array))
519 $_SESSION['role_memberships'] = $role_array;
521 $_SESSION['role_memberships'] = ACLRole::getUserRoleNames($user_id);
522 $role_array = $_SESSION['role_memberships'];
525 // else the session had the values, so we assign to the role array
527 $role_array = $_SESSION['role_memberships'];
531 // If the external cache didn't contain the values, we get them and put them in cache
533 $role_array = ACLRole::getUserRoleNames($user_id);
534 sugar_cache_put("RoleMemberships_".$user_id, $role_array);
538 // If the role doesn't exist in the list of the user's roles
539 if(!empty($role_array) && in_array($role_name, $role_array))
545 function get_summary_text() {
546 //$this->_create_proper_name_field();
552 * @param string $user_name - Must be non null and at least 2 characters
553 * @param string $user_password - Must be non null and at least 1 character.
554 * @desc Take an unencrypted username and password and return the encrypted password
555 * @return string encrypted password for storage in DB and comparison against DB password.
557 function encrypt_password($user_password)
559 // encrypt the password.
560 $salt = substr($this->user_name, 0, 2);
561 $encrypted_password = crypt($user_password, $salt);
563 return $encrypted_password;
567 * Authenicates the user; returns true if successful
569 * @param string $password MD5-encoded password
572 public function authenticate_user($password)
574 $row = self::findUserPassword($this->user_name, $password);
578 $this->id = $row['id'];
584 * retrieves an User bean
585 * preformat name & full_name attribute with first/last
586 * loads User's preferences
588 * @param string id ID of the User
589 * @param bool encode encode the result
590 * @return object User bean
591 * @return null null if no User found
593 function retrieve($id, $encode = true, $deleted = true) {
594 $ret = parent::retrieve($id, $encode, $deleted);
596 if (isset ($_SESSION)) {
597 $this->loadPreferences();
603 function retrieve_by_email_address($email) {
605 $email1= strtoupper($email);
608 select id from users where id in ( SELECT er.bean_id AS id FROM email_addr_bean_rel er,
609 email_addresses ea WHERE ea.id = er.email_address_id
610 AND ea.deleted = 0 AND er.deleted = 0 AND er.bean_module = 'Users' AND email_address_caps IN ('{$email1}') )
614 $res=$this->db->query($q);
615 $row=$this->db->fetchByAssoc($res);
617 if (!empty($row['id'])) {
618 return $this->retrieve($row['id']);
623 function bean_implements($interface) {
625 case 'ACL':return true;
632 * Load a user based on the user_name in $this
633 * @param string $user_password Password
634 * @param bool $password_encoded Is password md5-encoded or plain text?
635 * @return -- this if load was successul and null if load failed.
637 function load_user($user_password, $password_encoded = false)
640 unset($GLOBALS['login_error']);
641 if(isset ($_SESSION['loginattempts'])) {
642 $_SESSION['loginattempts'] += 1;
644 $_SESSION['loginattempts'] = 1;
646 if($_SESSION['loginattempts'] > 5) {
647 $GLOBALS['log']->fatal('SECURITY: '.$this->user_name.' has attempted to login '.$_SESSION['loginattempts'].' times from IP address: '.$_SERVER['REMOTE_ADDR'].'.');
651 $GLOBALS['log']->debug("Starting user load for $this->user_name");
653 if (!isset ($this->user_name) || $this->user_name == "" || !isset ($user_password) || $user_password == "")
656 if(!$password_encoded) {
657 $user_password = md5($user_password);
659 $row = self::findUserPassword($this->user_name, $user_password);
660 if(empty($row) || !empty ($GLOBALS['login_error'])) {
661 $GLOBALS['log']->fatal('SECURITY: User authentication for '.$this->user_name.' failed - could not Load User from Database');
665 // now fill in the fields.
666 $this->loadFromRow($row);
667 $this->loadPreferences();
669 require_once ('modules/Versions/CheckVersions.php');
670 $invalid_versions = get_invalid_versions();
672 if (!empty ($invalid_versions)) {
673 if (isset ($invalid_versions['Rebuild Relationships'])) {
674 unset ($invalid_versions['Rebuild Relationships']);
676 // flag for pickup in DisplayWarnings.php
677 $_SESSION['rebuild_relationships'] = true;
680 if (isset ($invalid_versions['Rebuild Extensions'])) {
681 unset ($invalid_versions['Rebuild Extensions']);
683 // flag for pickup in DisplayWarnings.php
684 $_SESSION['rebuild_extensions'] = true;
687 $_SESSION['invalid_versions'] = $invalid_versions;
689 if ($this->status != "Inactive")
690 $this->authenticated = true;
692 unset ($_SESSION['loginattempts']);
697 * Generate a new hash from plaintext password
698 * @param string $password
700 public static function getPasswordHash($password)
702 if(!defined('CRYPT_MD5') || !constant('CRYPT_MD5')) {
703 // does not support MD5 crypt - leave as is
704 if(defined('CRYPT_EXT_DES') && constant('CRYPT_EXT_DES')) {
705 return crypt(strtolower(md5($password)),
706 "_.012".substr(str_shuffle('./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'), -4));
708 // plain crypt cuts password to 8 chars, which is not enough
709 // fall back to old md5
710 return strtolower(md5($password));
712 return crypt(strtolower(md5($password)));
716 * Check that password matches existing hash
717 * @param string $password Plaintext password
718 * @param string $user_hash DB hash
720 public static function checkPassword($password, $user_hash)
722 return self::checkPasswordMD5(md5($password), $user_hash);
726 * Check that md5-encoded password matches existing hash
727 * @param string $password MD5-encoded password
728 * @param string $user_hash DB hash
729 * @return bool Match or not?
731 public static function checkPasswordMD5($password_md5, $user_hash)
733 if(empty($user_hash)) return false;
734 if($user_hash[0] != '$' && strlen($user_hash) == 32) {
735 // Old way - just md5 password
736 return strtolower($password_md5) == $user_hash;
738 return crypt(strtolower($password_md5), $user_hash) == $user_hash;
742 * Find user with matching password
743 * @param string $name Username
744 * @param string $password MD5-encoded password
745 * @param string $where Limiting query
746 * @return the matching User of false if not found
748 public static function findUserPassword($name, $password, $where = '')
751 $name = $db->quote($name);
752 $query = "SELECT * from users where user_name='$name'";
754 $query .= " AND $where";
756 $result = $db->limitQuery($query,0,1,false);
757 if(!empty($result)) {
758 $row = $db->fetchByAssoc($result);
759 if(self::checkPasswordMD5($password, $row['user_hash'])) {
767 * Sets new password and resets password expiration timers
768 * @param string $new_password
770 public function setNewPassword($new_password, $system_generated = '0')
772 $user_hash = self::getPasswordHash($new_password);
773 $this->setPreference('loginexpiration','0');
774 $this->setPreference('lockout','');
775 $this->setPreference('loginfailed','0');
776 $this->savePreferencesToDB();
778 $now = TimeDate::getInstance()->nowDb();
779 $query = "UPDATE $this->table_name SET user_hash='$user_hash', system_generated_password='$system_generated', pwd_last_changed='$now' where id='$this->id'";
780 $this->db->query($query, true, "Error setting new password for $this->user_name: ");
781 $_SESSION['hasExpiredPassword'] = '0';
785 * Verify that the current password is correct and write the new password to the DB.
787 * @param string $user name - Must be non null and at least 1 character.
788 * @param string $user_password - Must be non null and at least 1 character.
789 * @param string $new_password - Must be non null and at least 1 character.
790 * @return boolean - If passwords pass verification and query succeeds, return true, else return false.
792 function change_password($user_password, $new_password, $system_generated = '0')
795 global $current_user;
796 $GLOBALS['log']->debug("Starting password change for $this->user_name");
798 if (!isset ($new_password) || $new_password == "") {
799 $this->error_string = $mod_strings['ERR_PASSWORD_CHANGE_FAILED_1'].$current_user->user_name.$mod_strings['ERR_PASSWORD_CHANGE_FAILED_2'];
803 // Check new password against rules set by admin
804 if (!$this->check_password_rules($new_password)) {
805 $this->error_string = $mod_strings['ERR_PASSWORD_CHANGE_FAILED_1'].$current_user->user_name.$mod_strings['ERR_PASSWORD_CHANGE_FAILED_3'];
809 if (!$current_user->isAdminForModule('Users')) {
810 //check old password first
811 $row = self::findUserPassword($this->user_name, md5($user_password));
813 $GLOBALS['log']->warn("Incorrect old password for ".$this->user_name."");
814 $this->error_string = $mod_strings['ERR_PASSWORD_INCORRECT_OLD_1'].$this->user_name.$mod_strings['ERR_PASSWORD_INCORRECT_OLD_2'];
819 $this->setNewPassword($new_password, $system_generated);
824 * Check new password against rules set by admin
825 * @param string $password
828 function check_password_rules($password) {
829 $length = mb_strlen($password);
832 if(!empty($GLOBALS["sugar_config"]["passwordsetting"]["minpwdlength"]) && $GLOBALS["sugar_config"]["passwordsetting"]["minpwdlength"] > 0 && $length < $GLOBALS["sugar_config"]["passwordsetting"]["minpwdlength"]) {
837 if(!empty($GLOBALS['sugar_config']['passwordsetting']['maxpwdlength']) && $GLOBALS['sugar_config']['passwordsetting']['maxpwdlength'] > 0 && $length > $GLOBALS['sugar_config']['passwordsetting']['maxpwdlength']) {
842 if(!empty($GLOBALS["sugar_config"]["passwordsetting"]["onelower"]) && !preg_match('/[a-z]+/', $password)){
847 if(!empty($GLOBALS["sugar_config"]["passwordsetting"]["oneupper"]) && !preg_match('/[A-Z]+/', $password)){
852 if(!empty($GLOBALS["sugar_config"]["passwordsetting"]["onenumber"]) && !preg_match('/[0-9]+/', $password)){
856 // One special character
857 if(!empty($GLOBALS["sugar_config"]["passwordsetting"]["onespecial"]) && !preg_match('/[|}{~!@#$%^&*()_+=-]+/', $password)){
862 if(!empty($GLOBALS["sugar_config"]["passwordsetting"]["customregex"]) && !preg_match($GLOBALS["sugar_config"]["passwordsetting"]["customregex"], $password)){
869 function is_authenticated() {
870 return $this->authenticated;
873 function fill_in_additional_list_fields() {
874 $this->fill_in_additional_detail_fields();
877 function fill_in_additional_detail_fields() {
878 // jmorais@dri Bug #56269
879 parent::fill_in_additional_detail_fields();
883 $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";
884 $result = $this->db->query($query, true, "Error filling in additional detail fields");
886 $row = $this->db->fetchByAssoc($result);
889 $this->reports_to_name = stripslashes($row['first_name'].' '.$row['last_name']);
891 $this->reports_to_name = '';
895 $this->_create_proper_name_field();
898 public function retrieve_user_id(
902 $userFocus = new User;
903 $userFocus->retrieve_by_string_fields(array('user_name'=>$user_name));
904 if ( empty($userFocus->id) )
907 return $userFocus->id;
911 * @return -- returns a list of all users in the system.
912 * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc..
913 * All Rights Reserved..
914 * Contributor(s): ______________________________________..
916 function verify_data($ieVerified=true) {
917 global $mod_strings, $current_user;
920 if (!empty ($this->id)) {
921 // Make sure the user doesn't report to themselves.
922 $reports_to_self = 0;
923 $check_user = $this->reports_to_id;
924 $already_seen_list = array ();
925 while (!empty ($check_user)) {
926 if (isset ($already_seen_list[$check_user])) {
927 // This user doesn't actually report to themselves
928 // But someone above them does.
929 $reports_to_self = 1;
932 if ($check_user == $this->id) {
933 $reports_to_self = 1;
936 $already_seen_list[$check_user] = 1;
937 $query = "SELECT reports_to_id FROM users WHERE id='".$this->db->quote($check_user)."'";
938 $result = $this->db->query($query, true, "Error checking for reporting-loop");
939 $row = $this->db->fetchByAssoc($result);
940 echo ("fetched: ".$row['reports_to_id']." from ".$check_user."<br>");
941 $check_user = $row['reports_to_id'];
944 if ($reports_to_self == 1) {
945 $this->error_string .= $mod_strings['ERR_REPORT_LOOP'];
950 $query = "SELECT user_name from users where user_name='$this->user_name' AND deleted=0";
951 if(!empty($this->id))$query .= " AND id<>'$this->id'";
952 $result = $this->db->query($query, true, "Error selecting possible duplicate users: ");
953 $dup_users = $this->db->fetchByAssoc($result);
955 if (!empty($dup_users)) {
956 $this->error_string .= $mod_strings['ERR_USER_NAME_EXISTS_1'].$this->user_name.$mod_strings['ERR_USER_NAME_EXISTS_2'];
960 if (is_admin($current_user)) {
961 $remaining_admins = $this->db->getOne("SELECT COUNT(*) as c from users where is_admin = 1 AND deleted=0");
963 if (($remaining_admins <= 1) && ($this->is_admin != '1') && ($this->id == $current_user->id)) {
964 $GLOBALS['log']->debug("Number of remaining administrator accounts: {$remaining_admins}");
965 $this->error_string .= $mod_strings['ERR_LAST_ADMIN_1'].$this->user_name.$mod_strings['ERR_LAST_ADMIN_2'];
969 ///////////////////////////////////////////////////////////////////////
970 //// InboundEmail verification failure
973 $this->error_string .= '<br />'.$mod_strings['ERR_EMAIL_NO_OPTS'];
979 function get_list_view_data() {
981 global $current_user, $mod_strings;
982 // Bug #48555 Not User Name Format of User's locale.
983 $this->_create_proper_name_field();
985 $user_fields = $this->get_list_view_array();
987 $user_fields['IS_ADMIN_IMAGE'] = SugarThemeRegistry::current()->getImage('check_inline', '',null,null,'.gif',$mod_strings['LBL_CHECKMARK']);
988 elseif (!$this->is_admin) $user_fields['IS_ADMIN'] = '';
990 $user_fields['IS_GROUP_IMAGE'] = SugarThemeRegistry::current()->getImage('check_inline', '',null,null,'.gif',$mod_strings['LBL_CHECKMARK']);
992 $user_fields['IS_GROUP_IMAGE'] = '';
995 if ($this->is_admin) {
996 $user_fields['IS_ADMIN_IMAGE'] = SugarThemeRegistry::current()->getImage('check_inline', '',null,null,'.gif',translate('LBL_CHECKMARK', 'Users'));
997 } elseif (!$this->is_admin) {
998 $user_fields['IS_ADMIN'] = '';
1001 if ($this->is_group) {
1002 $user_fields['IS_GROUP_IMAGE'] = SugarThemeRegistry::current()->getImage('check_inline', '',null,null,'.gif',translate('LBL_CHECKMARK', 'Users'));
1004 $user_fields['NAME'] = empty ($this->name) ? '' : $this->name;
1007 $user_fields['REPORTS_TO_NAME'] = $this->reports_to_name;
1009 $user_fields['EMAIL1'] = $this->emailAddress->getPrimaryAddress($this);
1011 return $user_fields;
1014 function list_view_parse_additional_sections(& $list_form, $xTemplateSection) {
1024 * Returns all active and inactive users
1025 * @return Array of all users in the system
1028 public static function getAllUsers()
1030 $active_users = get_user_array(FALSE);
1031 $inactive_users = get_user_array(FALSE, "Inactive");
1032 $result = $active_users + $inactive_users;
1037 function create_export_query($order_by, $where) {
1038 include('modules/Users/field_arrays.php');
1041 foreach($fields_array['User']['export_fields'] as $field) {
1042 $cols .= (empty($cols)) ? '' : ', ';
1046 $query = "SELECT {$cols} FROM users ";
1048 $where_auto = " users.deleted = 0";
1051 $query .= " WHERE $where AND ".$where_auto;
1053 $query .= " WHERE ".$where_auto;
1055 // admin for module user is not be able to export a super-admin
1056 global $current_user;
1057 if(!$current_user->is_admin){
1058 $query .= " AND users.is_admin=0";
1061 if ($order_by != "")
1062 $query .= " ORDER BY $order_by";
1064 $query .= " ORDER BY users.user_name";
1069 /** Returns a list of the associated users
1070 * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc..
1071 * All Rights Reserved..
1072 * Contributor(s): ______________________________________..
1074 function get_meetings() {
1075 // First, get the list of IDs.
1076 $query = "SELECT meeting_id as id from meetings_users where user_id='$this->id' AND deleted=0";
1077 return $this->build_related_list($query, new Meeting());
1079 function get_calls() {
1080 // First, get the list of IDs.
1081 $query = "SELECT call_id as id from calls_users where user_id='$this->id' AND deleted=0";
1082 return $this->build_related_list($query, new Call());
1086 * generates Javascript to display I-E mail counts, both personal and group
1088 function displayEmailCounts() {
1090 $new = translate('LBL_NEW', 'Emails');
1091 $default = 'index.php?module=Emails&action=ListView&assigned_user_id='.$this->id;
1093 $verts = array('Love', 'Links', 'Pipeline', 'RipCurl', 'SugarLite');
1095 if($this->hasPersonalEmail()) {
1096 $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\'');
1097 $a = $this->db->fetchByAssoc($r);
1098 if(in_array($theme, $verts)) {
1101 $count .= ' ';
1103 $count .= '<a href='.$default.'&type=inbound>'.translate('LBL_LIST_TITLE_MY_INBOX', 'Emails').': ('.$a['c'].' '.$new.')</a>';
1105 if(!in_array($theme, $verts)) {
1110 $r = $this->db->query('SELECT id FROM users WHERE users.is_group = 1 AND deleted = 0');
1113 while($a = $this->db->fetchByAssoc($r)) {
1114 if($groupIds != '') {$groupIds .= ', ';}
1115 $groupIds .= "'".$a['id']."'";
1119 if(strlen($groupIds) > 0) {
1120 $groupQuery = 'SELECT count(*) AS c FROM emails ';
1121 $groupQuery .= ' WHERE emails.deleted=0 AND emails.assigned_user_id IN ('.$groupIds.') AND emails.type = \'inbound\' AND emails.status = \'unread\'';
1122 $r = $this->db->query($groupQuery);
1123 if(is_resource($r)) {
1124 $a = $this->db->fetchByAssoc($r);
1130 if(in_array($theme, $verts)) $count .= '<br />';
1131 if(empty($count)) $count .= ' ';
1132 $count .= '<a href=index.php?module=Emails&action=ListViewGroup>'.translate('LBL_LIST_TITLE_GROUP_INBOX', 'Emails').': ('.$total.' '.$new.')</a>';
1134 $out = '<script type="text/javascript" language="Javascript">';
1135 $out .= 'var welcome = document.getElementById("welcome");';
1136 $out .= 'var welcomeContent = welcome.innerHTML;';
1137 $out .= 'welcome.innerHTML = welcomeContent + "'.$count.'";';
1138 $out .= '</script>';
1143 function getPreferredEmail() {
1145 $nameEmail = $this->getUsersNameAndEmail();
1146 $prefAddr = $nameEmail['email'];
1147 $fullName = $nameEmail['name'];
1148 if (empty ($prefAddr)) {
1149 $nameEmail = $this->getSystemDefaultNameAndEmail();
1150 $prefAddr = $nameEmail['email'];
1151 $fullName = $nameEmail['name'];
1153 $fullName = from_html($fullName);
1154 $ret['name'] = $fullName;
1155 $ret['email'] = $prefAddr;
1159 function getUsersNameAndEmail()
1161 // Bug #48555 Not User Name Format of User's locale.
1162 $this->_create_proper_name_field();
1164 $prefAddr = $this->emailAddress->getPrimaryAddress($this);
1166 if (empty ($prefAddr)) {
1167 $prefAddr = $this->emailAddress->getReplyToAddress($this);
1169 return array('email' => $prefAddr , 'name' => $this->name);
1173 function getSystemDefaultNameAndEmail() {
1175 $email = new Email();
1176 $return = $email->getSystemDefaultEmail();
1177 $prefAddr = $return['email'];
1178 $fullName = $return['name'];
1179 return array('email' => $prefAddr , 'name' => $fullName);
1183 * sets User email default in config.php if not already set by install - i.
1186 function setDefaultsInConfig() {
1187 global $sugar_config;
1188 $sugar_config['email_default_client'] = 'sugar';
1189 $sugar_config['email_default_editor'] = 'html';
1190 ksort($sugar_config);
1191 write_array_to_file('sugar_config', $sugar_config, 'config.php');
1192 return $sugar_config;
1196 * returns User's email address based on descending order of preferences
1198 * @param string id GUID of target user if needed
1199 * @return array Assoc array for an email and name
1201 function getEmailInfo($id='') {
1205 $user->retrieve($id);
1209 $fromName = $user->getPreference('mail_fromname');
1210 if(empty($fromName)) {
1211 // cn: bug 8586 - localized name format
1212 $fromName = $user->full_name;
1216 $fromaddr = $user->getPreference('mail_fromaddress');
1217 if(empty($fromaddr)) {
1218 if(!empty($user->email1) && isset($user->email1)) {
1219 $fromaddr = $user->email1;
1220 } elseif(!empty($user->email2) && isset($user->email2)) {
1221 $fromaddr = $user->email2;
1223 $r = $user->db->query("SELECT value FROM config WHERE name = 'fromaddress'");
1224 $a = $user->db->fetchByAssoc($r);
1225 $fromddr = $a['value'];
1229 $ret['name'] = $fromName;
1230 $ret['email'] = $fromaddr;
1236 * returns opening <a href=xxxx for a contact, account, etc
1237 * cascades from User set preference to System-wide default
1238 * @return string link
1239 * @param attribute the email addy
1240 * @param focus the parent bean
1242 * @param return_module
1243 * @param return_action
1247 function getEmailLink2($emailAddress, &$focus, $contact_id='', $ret_module='', $ret_action='DetailView', $ret_id='', $class='') {
1249 global $sugar_config;
1251 if(!isset($sugar_config['email_default_client'])) {
1252 $this->setDefaultsInConfig();
1255 $userPref = $this->getPreference('email_link_type');
1256 $defaultPref = $sugar_config['email_default_client'];
1257 if($userPref != '') {
1258 $client = $userPref;
1260 $client = $defaultPref;
1263 if($client == 'sugar') {
1266 $to_addrs_names = '';
1267 $to_addrs_emails = '';
1269 $fullName = !empty($focus->name) ? $focus->name : '';
1271 if(empty($ret_module)) $ret_module = $focus->module_dir;
1272 if(empty($ret_id)) $ret_id = $focus->id;
1273 if($focus->object_name == 'Contact') {
1274 $contact_id = $focus->id;
1275 $to_addrs_ids = $focus->id;
1276 // Bug #48555 Not User Name Format of User's locale.
1277 $focus->_create_proper_name_field();
1278 $fullName = $focus->name;
1279 $to_addrs_names = $fullName;
1280 $to_addrs_emails = $focus->email1;
1283 $emailLinkUrl = 'contact_id='.$contact_id.
1284 '&parent_type='.$focus->module_dir.
1285 '&parent_id='.$focus->id.
1286 '&parent_name='.urlencode($fullName).
1287 '&to_addrs_ids='.$to_addrs_ids.
1288 '&to_addrs_names='.urlencode($to_addrs_names).
1289 '&to_addrs_emails='.urlencode($to_addrs_emails).
1290 '&to_email_addrs='.urlencode($fullName . ' <' . $emailAddress . '>').
1291 '&return_module='.$ret_module.
1292 '&return_action='.$ret_action.
1293 '&return_id='.$ret_id;
1295 //Generate the compose package for the quick create options.
1296 //$json = getJSONobj();
1297 //$composeOptionsLink = $json->encode( array('composeOptionsLink' => $emailLinkUrl,'id' => $focus->id) );
1298 require_once('modules/Emails/EmailUI.php');
1299 $eUi = new EmailUI();
1300 $j_quickComposeOptions = $eUi->generateComposePackageForQuickCreateFromComposeUrl($emailLinkUrl, true);
1302 $emailLink = "<a href='javascript:void(0);' onclick='SUGAR.quickCompose.init($j_quickComposeOptions);' class='$class'>";
1306 $emailLink = '<a href="mailto:'.$emailAddress.'" class="'.$class.'">';
1313 * returns opening <a href=xxxx for a contact, account, etc
1314 * cascades from User set preference to System-wide default
1315 * @return string link
1316 * @param attribute the email addy
1317 * @param focus the parent bean
1319 * @param return_module
1320 * @param return_action
1324 function getEmailLink($attribute, &$focus, $contact_id='', $ret_module='', $ret_action='DetailView', $ret_id='', $class='') {
1326 global $sugar_config;
1328 if(!isset($sugar_config['email_default_client'])) {
1329 $this->setDefaultsInConfig();
1332 $userPref = $this->getPreference('email_link_type');
1333 $defaultPref = $sugar_config['email_default_client'];
1334 if($userPref != '') {
1335 $client = $userPref;
1337 $client = $defaultPref;
1340 if($client == 'sugar') {
1343 $to_addrs_names = '';
1344 $to_addrs_emails = '';
1346 $fullName = !empty($focus->name) ? $focus->name : '';
1348 if(!empty($focus->$attribute)) {
1349 $email = $focus->$attribute;
1353 if(empty($ret_module)) $ret_module = $focus->module_dir;
1354 if(empty($ret_id)) $ret_id = $focus->id;
1355 if($focus->object_name == 'Contact') {
1356 // Bug #48555 Not User Name Format of User's locale.
1357 $focus->_create_proper_name_field();
1358 $fullName = $focus->name;
1359 $contact_id = $focus->id;
1360 $to_addrs_ids = $focus->id;
1361 $to_addrs_names = $fullName;
1362 $to_addrs_emails = $focus->email1;
1365 $emailLinkUrl = 'contact_id='.$contact_id.
1366 '&parent_type='.$focus->module_dir.
1367 '&parent_id='.$focus->id.
1368 '&parent_name='.urlencode($fullName).
1369 '&to_addrs_ids='.$to_addrs_ids.
1370 '&to_addrs_names='.urlencode($to_addrs_names).
1371 '&to_addrs_emails='.urlencode($to_addrs_emails).
1372 '&to_email_addrs='.urlencode($fullName . ' <' . $email . '>').
1373 '&return_module='.$ret_module.
1374 '&return_action='.$ret_action.
1375 '&return_id='.$ret_id;
1377 //Generate the compose package for the quick create options.
1378 require_once('modules/Emails/EmailUI.php');
1379 $eUi = new EmailUI();
1380 $j_quickComposeOptions = $eUi->generateComposePackageForQuickCreateFromComposeUrl($emailLinkUrl, true);
1381 $emailLink = "<a href='javascript:void(0);' onclick='SUGAR.quickCompose.init($j_quickComposeOptions);' class='$class'>";
1385 $emailLink = '<a href="mailto:'.$focus->$attribute.'" class="'.$class.'">';
1393 * gets a human-readable explanation of the format macro
1394 * @return string Human readable name format
1396 function getLocaleFormatDesc() {
1398 global $mod_strings;
1399 global $app_strings;
1401 $format['f'] = $mod_strings['LBL_LOCALE_DESC_FIRST'];
1402 $format['l'] = $mod_strings['LBL_LOCALE_DESC_LAST'];
1403 $format['s'] = $mod_strings['LBL_LOCALE_DESC_SALUTATION'];
1404 $format['t'] = $mod_strings['LBL_LOCALE_DESC_TITLE'];
1406 $name['f'] = $app_strings['LBL_LOCALE_NAME_EXAMPLE_FIRST'];
1407 $name['l'] = $app_strings['LBL_LOCALE_NAME_EXAMPLE_LAST'];
1408 $name['s'] = $app_strings['LBL_LOCALE_NAME_EXAMPLE_SALUTATION'];
1409 $name['t'] = $app_strings['LBL_LOCALE_NAME_EXAMPLE_TITLE'];
1411 $macro = $locale->getLocaleFormatMacro();
1415 for($i=0; $i<strlen($macro); $i++) {
1416 if(array_key_exists($macro{$i}, $format)) {
1417 $ret1 .= "<i>".$format[$macro{$i}]."</i>";
1418 $ret2 .= "<i>".$name[$macro{$i}]."</i>";
1420 $ret1 .= $macro{$i};
1421 $ret2 .= $macro{$i};
1424 return $ret1."<br />".$ret2;
1430 * Here are the multi level admin access check functions.
1434 * Helper function to remap some modules around ACL wise
1438 protected function _fixupModuleForACL($module) {
1439 if($module=='ContractTypes') {
1440 $module = 'Contracts';
1442 if(preg_match('/Product[a-zA-Z]*/',$module)) {
1443 $module = 'Products';
1449 * Helper function that enumerates the list of modules and checks if they are an admin/dev.
1450 * The code was just too similar to copy and paste.
1454 protected function _getModulesForACL($type='dev'){
1455 $isDev = $type=='dev';
1456 $isAdmin = $type=='admin';
1459 $myModules = array();
1461 if (!is_array($beanList) ) {
1465 // These modules don't take kindly to the studio trying to play about with them.
1466 static $ignoredModuleList = array('iFrames','Feeds','Home','Dashboard','Calendar','Activities','Reports');
1469 $actions = ACLAction::getUserActions($this->id);
1471 foreach ($beanList as $module=>$val) {
1472 // Remap the module name
1473 $module = $this->_fixupModuleForACL($module);
1474 if (in_array($module,$myModules)) {
1475 // Already have the module in the list
1478 if (in_array($module,$ignoredModuleList)) {
1479 // You can't develop on these modules.
1483 $focus = SugarModule::get($module)->loadBean();
1484 if ( $focus instanceOf SugarBean ) {
1485 $key = $focus->acltype;
1490 if (($this->isAdmin() && isset($actions[$module][$key]))
1492 $myModules[] = $module;
1499 * Is this user a system wide admin
1503 public function isAdmin() {
1504 if(isset($this->is_admin)
1505 &&($this->is_admin == '1' || $this->is_admin === 'on')){
1511 * Is this user a developer for any module
1515 public function isDeveloperForAnyModule() {
1516 if(empty($this->id)) {
1517 // empty user is no developer
1520 if ($this->isAdmin()) {
1526 * List the modules a user has developer access to
1530 public function getDeveloperModules() {
1531 static $developerModules;
1532 if (!isset($_SESSION[$this->user_name.'_get_developer_modules_for_user']) ) {
1533 $_SESSION[$this->user_name.'_get_developer_modules_for_user'] = $this->_getModulesForACL('dev');
1536 return $_SESSION[$this->user_name.'_get_developer_modules_for_user'];
1539 * Is this user a developer for the specified module
1543 public function isDeveloperForModule($module) {
1544 if(empty($this->id)) {
1545 // empty user is no developer
1548 if ($this->isAdmin()) {
1552 $devModules = $this->getDeveloperModules();
1554 $module = $this->_fixupModuleForACL($module);
1556 if (in_array($module,$devModules) ) {
1563 * List the modules a user has admin access to
1567 public function getAdminModules() {
1568 if (!isset($_SESSION[$this->user_name.'_get_admin_modules_for_user']) ) {
1569 $_SESSION[$this->user_name.'_get_admin_modules_for_user'] = $this->_getModulesForACL('admin');
1572 return $_SESSION[$this->user_name.'_get_admin_modules_for_user'];
1575 * Is this user an admin for the specified module
1579 public function isAdminForModule($module) {
1580 if(empty($this->id)) {
1581 // empty user is no admin
1584 if ($this->isAdmin()) {
1588 $adminModules = $this->getAdminModules();
1590 $module = $this->_fixupModuleForACL($module);
1592 if (in_array($module,$adminModules) ) {
1599 * Whether or not based on the user's locale if we should show the last name first.
1603 public function showLastNameFirst(){
1605 $localeFormat = $locale->getLocaleFormatMacro($this);
1606 if ( strpos($localeFormat,'l') > strpos($localeFormat,'f') ) {
1613 function create_new_list_query($order_by, $where,$filter=array(),$params=array(), $show_deleted = 0,$join_type='', $return_array = false,$parentbean=null, $singleSelect = false)
1614 { //call parent method, specifying for array to be returned
1615 $ret_array = parent::create_new_list_query($order_by, $where,$filter,$params, $show_deleted,$join_type, true,$parentbean, $singleSelect);
1617 //if this is being called from webservices, then run additional code
1618 if(!empty($GLOBALS['soap_server_object'])){
1620 //if this is a single select, then secondary queries are being run that may result in duplicate rows being returned through the
1621 //left joins with meetings/tasks/call. We need to change the left joins to include a null check (bug 40250)
1624 //retrieve the 'from' string and make lowercase for easier manipulation
1625 $left_str = strtolower($ret_array['from']);
1626 $lefts = explode('left join', $left_str);
1629 //explode on the left joins and process each one
1630 foreach($lefts as $ljVal){
1631 //grab the join alias
1632 $onPos = strpos( $ljVal, ' on');
1633 if($onPos === false){
1634 $new_left_str .=' '.$ljVal.' ';
1637 $spacePos = strrpos(substr($ljVal, 0, $onPos),' ');
1638 $alias = substr($ljVal,$spacePos,$onPos-$spacePos);
1640 //add null check to end of the Join statement
1641 // Bug #46390 to use id_c field instead of id field for custom tables
1642 if(substr($alias, -5) != '_cstm')
1644 $ljVal =' LEFT JOIN '.$ljVal.' and '.$alias.'.id is null ';
1648 $ljVal =' LEFT JOIN '.$ljVal.' and '.$alias.'.id_c is null ';
1651 //add statement into new string
1652 $new_left_str .= $ljVal;
1654 //replace the old string with the new one
1655 $ret_array['from'] = $new_left_str;
1659 //return array or query string
1665 return $ret_array['select'] . $ret_array['from'] . $ret_array['where']. $ret_array['order_by'];
1672 * Get user first day of week.
1674 * @param [User] $user user object, current user if not specified
1675 * @return int : 0 = Sunday, 1 = Monday, etc...
1677 public function get_first_day_of_week()
1679 $fdow = $this->getPreference('fdow');
1689 * Method for password generation
1692 * @return string password
1694 public static function generatePassword()
1696 $res = $GLOBALS['sugar_config']['passwordsetting'];
1698 //chars to select from
1699 $LOWERCASE = "abcdefghijklmnpqrstuvwxyz";
1700 $NUMBER = "0123456789";
1701 $UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1702 $SPECIAL = '~!@#$%^&*()_+=-{}|';
1704 $charBKT .= $UPPERCASE . $LOWERCASE . $NUMBER;
1708 // Create random characters for the ones that doesnt have requirements
1709 for ($i=0; $i < $length - $condition; $i ++) // loop and create password
1711 $password = $password . substr ($charBKT, rand() % strlen($charBKT), 1);
1718 * Send new password or link to user
1720 * @param string $templateId Id of email template
1721 * @param array $additionalData additional params: link, url, password
1722 * @return array status: true|false, message: error message, if status = false and message = '' it means that send method has returned false
1724 public function sendEmailForPassword($templateId, array $additionalData = array())
1726 global $sugar_config, $current_user;
1727 $mod_strings = return_module_language('', 'Users');
1733 $emailTemp = new EmailTemplate();
1734 $emailTemp->disable_row_level_security = true;
1735 if ($emailTemp->retrieve($templateId) == '')
1737 $result['message'] = $mod_strings['LBL_EMAIL_TEMPLATE_MISSING'];
1741 //replace instance variables in email templates
1742 $htmlBody = $emailTemp->body_html;
1743 $body = $emailTemp->body;
1744 if (isset($additionalData['link']) && $additionalData['link'] == true)
1746 $htmlBody = str_replace('$contact_user_link_guid', $additionalData['url'], $htmlBody);
1747 $body = str_replace('$contact_user_link_guid', $additionalData['url'], $body);
1751 $htmlBody = str_replace('$contact_user_user_hash', $additionalData['password'], $htmlBody);
1752 $body = str_replace('$contact_user_user_hash', $additionalData['password'], $body);
1754 // Bug 36833 - Add replacing of special value $instance_url
1755 $htmlBody = str_replace('$config_site_url', $sugar_config['site_url'], $htmlBody);
1756 $body = str_replace('$config_site_url', $sugar_config['site_url'], $body);
1758 $htmlBody = str_replace('$contact_user_user_name', $this->user_name, $htmlBody);
1759 $htmlBody = str_replace('$contact_user_pwd_last_changed', TimeDate::getInstance()->nowDb(), $htmlBody);
1760 $body = str_replace('$contact_user_user_name', $this->user_name, $body);
1761 $body = str_replace('$contact_user_pwd_last_changed', TimeDate::getInstance()->nowDb(), $body);
1762 $emailTemp->body_html = $htmlBody;
1763 $emailTemp->body = $body;
1765 $itemail = $this->emailAddress->getPrimaryAddress($this);
1766 //retrieve IT Admin Email
1767 //_ppd( $emailTemp->body_html);
1768 //retrieve email defaults
1769 $emailObj = new Email();
1770 $defaults = $emailObj->getSystemDefaultEmail();
1771 require_once('include/SugarPHPMailer.php');
1772 $mail = new SugarPHPMailer();
1773 $mail->setMailerForSystem();
1774 //$mail->IsHTML(true);
1775 $mail->From = $defaults['email'];
1776 $mail->FromName = $defaults['name'];
1777 $mail->ClearAllRecipients();
1778 $mail->ClearReplyTos();
1779 $mail->Subject = from_html($emailTemp->subject);
1780 if ($emailTemp->text_only != 1)
1782 $mail->IsHTML(true);
1783 $mail->Body = from_html($emailTemp->body_html);
1784 $mail->AltBody = from_html($emailTemp->body);
1788 $mail->Body_html = from_html($emailTemp->body_html);
1789 $mail->Body = from_html($emailTemp->body);
1791 if ($mail->Body == '' && $current_user->is_admin)
1793 global $app_strings;
1794 $result['message'] = $app_strings['LBL_EMAIL_TEMPLATE_EDIT_PLAIN_TEXT'];
1797 if ($mail->Mailer == 'smtp' && $mail->Host =='' && $current_user->is_admin)
1799 $result['message'] = $mod_strings['ERR_SERVER_SMTP_EMPTY'];
1803 $mail->prepForOutbound();
1804 $hasRecipients = false;
1806 if (!empty($itemail))
1810 $mail->AddBCC($itemail);
1814 $mail->AddAddress($itemail);
1816 $hasRecipients = true;
1820 $result['status'] = @$mail->Send();
1823 if ($result['status'] == true)
1825 $emailObj->team_id = 1;
1826 $emailObj->to_addrs = '';
1827 $emailObj->type = 'archived';
1828 $emailObj->deleted = '0';
1829 $emailObj->name = $mail->Subject ;
1830 $emailObj->description = $mail->Body;
1831 $emailObj->description_html = null;
1832 $emailObj->from_addr = $mail->From;
1833 $emailObj->parent_type = 'User';
1834 $emailObj->date_sent = TimeDate::getInstance()->nowDb();
1835 $emailObj->modified_user_id = '1';
1836 $emailObj->created_by = '1';
1837 $emailObj->status = 'sent';
1839 if (!isset($additionalData['link']) || $additionalData['link'] == false)
1841 $this->setNewPassword($additionalData['password'], '1');
1848 // Bug #48014 Must to send password to imported user if this action is required
1849 function afterImportSave()
1852 $this->user_hash == false
1854 && !$this->portal_only
1855 && isset($GLOBALS['sugar_config']['passwordsetting']['SystemGeneratedPasswordON'])
1856 && $GLOBALS['sugar_config']['passwordsetting']['SystemGeneratedPasswordON']
1859 $backUpPost = $_POST;
1861 'userId' => $this->id
1864 require('modules/Users/GeneratePassword.php');
1865 $result = ob_get_clean();
1866 $_POST = $backUpPost;
1867 return $result == true;