2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4 * SugarCRM is a customer relationship management program developed by
5 * SugarCRM, Inc. Copyright (C) 2004-2011 SugarCRM Inc.
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 /*********************************************************************************
40 * Description: TODO: To be written.
41 * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc.
42 * All Rights Reserved.
43 * Contributor(s): ______________________________________..
44 ********************************************************************************/
46 require_once('include/SugarObjects/templates/person/Person.php');
49 // User is used to store customer information.
50 class User extends Person {
62 var $modified_user_id;
65 var $modified_by_name;
77 var $address_postalcode;
83 var $authenticated = false;
90 var $accept_status; // to support Meetings
91 //adding a property called team_id so we can populate it for use in the team widget
94 var $receive_notifications;
98 var $team_exists = false;
99 var $table_name = "users";
100 var $module_dir = 'Users';
101 var $object_name = "User";
102 var $user_preferences;
104 var $importable = true;
105 var $_userPreferenceFocus;
107 var $encodeFields = Array ("first_name", "last_name", "description");
109 // This is used to retrieve related fields from form posts.
110 var $additional_column_fields = array ('reports_to_name'
116 var $new_schema = true;
121 $this->_loadUserPreferencesFocus();
124 protected function _loadUserPreferencesFocus()
126 $this->_userPreferenceFocus = new UserPreference($this);
130 * returns an admin user
132 public function getSystemUser()
134 if (null === $this->retrieve('1'))
135 // handle cases where someone deleted user with id "1"
136 $this->retrieve_by_string_fields(array(
137 'status' => 'Active',
146 * convenience function to get user's default signature
148 function getDefaultSignature() {
149 if($defaultId = $this->getPreference('signature_default')) {
150 return $this->getSignature($defaultId);
157 * retrieves the signatures for a user
158 * @param string id ID of user_signature
159 * @return array ID, signature, and signature_html
161 public function getSignature($id)
163 $signatures = $this->getSignaturesArray();
165 return $signatures[$id];
168 function getSignaturesArray() {
169 $q = 'SELECT * FROM users_signatures WHERE user_id = \''.$this->id.'\' AND deleted = 0 ORDER BY name ASC';
170 $r = $this->db->query($q);
173 $sig = array(""=>"");
175 while($a = $this->db->fetchByAssoc($r)) {
183 * retrieves any signatures that the User may have created as <select>
185 public function getSignatures(
191 $sig = $this->getSignaturesArray();
193 foreach ($sig as $key => $arr)
195 $sigs[$key] = !empty($arr['name']) ? $arr['name'] : '';
200 $change = ($forSettings) ? "onChange='displaySignatureEdit();'" : "onChange='setSigEditButtonVisibility();'";
203 $id = (!$forSettings) ? 'signature_id' : 'signature_idDisplay';
205 $out = "<select {$change} id='{$id}' name='{$id}'>";
206 $out .= get_select_options_with_id($sigs, $defaultSig).'</select>';
212 * returns buttons and JS for signatures
214 function getSignatureButtons($jscall='', $defaultDisplay=false) {
217 $jscall = empty($jscall) ? 'open_email_signature_form' : $jscall;
219 $butts = "<input class='button' onclick='javascript:{$jscall}(\"\", \"{$this->id}\");' value='{$mod_strings['LBL_BUTTON_CREATE']}' type='button'> ";
220 if($defaultDisplay) {
221 $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">
224 $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">
231 * performs a rudimentary check to verify if a given user has setup personal
236 public function hasPersonalEmail()
238 $focus = new InboundEmail;
239 $focus->retrieve_by_string_fields(array('group_id' => $this->id));
241 return !empty($focus->id);
244 /* Returns the User's private GUID; this is unassociated with the User's
245 * actual GUID. It is used to secure file names that must be HTTP://
246 * accesible, but obfusicated.
248 function getUserPrivGuid() {
249 $userPrivGuid = $this->getPreference('userPrivGuid', 'global', $this);
251 return $userPrivGuid;
253 $this->setUserPrivGuid();
254 if (!isset ($_SESSION['setPrivGuid'])) {
255 $_SESSION['setPrivGuid'] = true;
256 $userPrivGuid = $this->getUserPrivGuid();
257 return $userPrivGuid;
259 sugar_die("Breaking Infinite Loop Condition: Could not setUserPrivGuid.");
264 function setUserPrivGuid() {
265 $privGuid = create_guid();
266 //($name, $value, $nosession=0)
267 $this->setPreference('userPrivGuid', $privGuid, 0, 'global', $this);
271 * Interface for the User object to calling the UserPreference::setPreference() method in modules/UserPreferences/UserPreference.php
273 * @see UserPreference::setPreference()
275 * @param string $name Name of the preference to set
276 * @param string $value Value to set preference to
277 * @param null $nosession For BC, ignored
278 * @param string $category Name of the category to retrieve
280 public function setPreference(
288 if ( func_num_args() > 4 ) {
289 $user = func_get_arg(4);
290 $GLOBALS['log']->deprecated('User::setPreferences() should not be used statically.');
295 $user->_userPreferenceFocus->setPreference($name, $value, $category);
299 * Interface for the User object to calling the UserPreference::resetPreferences() method in modules/UserPreferences/UserPreference.php
301 * @see UserPreference::resetPreferences()
303 * @param string $category category to reset
305 public function resetPreferences(
310 if ( func_num_args() > 1 ) {
311 $user = func_get_arg(1);
312 $GLOBALS['log']->deprecated('User::resetPreferences() should not be used statically.');
317 $user->_userPreferenceFocus->resetPreferences($category);
322 * Interface for the User object to calling the UserPreference::isPreferenceSizeTooLarge() method in modules/UserPreferences/UserPreference.php
324 * @see UserPreference::isPreferenceSizeTooLarge()
326 * @param string $category category to check
328 public function isPreferenceSizeTooLarge(
333 if ( func_num_args() > 1 ) {
334 $user = func_get_arg(1);
335 $GLOBALS['log']->deprecated('User::resetPreferences() should not be used statically.');
340 return $user->_userPreferenceFocus->isPreferenceSizeTooLarge($category);
345 * Interface for the User object to calling the UserPreference::savePreferencesToDB() method in modules/UserPreferences/UserPreference.php
347 * @see UserPreference::savePreferencesToDB()
349 public function savePreferencesToDB()
352 if ( func_num_args() > 0 ) {
353 $user = func_get_arg(0);
354 $GLOBALS['log']->deprecated('User::savePreferencesToDB() should not be used statically.');
359 $user->_userPreferenceFocus->savePreferencesToDB();
363 * Unconditionally reloads user preferences from the DB and updates the session
364 * @param string $category name of the category to retreive, defaults to global scope
365 * @return bool successful?
367 public function reloadPreferences($category = 'global')
369 return $this->_userPreferenceFocus->reloadPreferences($category = 'global');
373 * Interface for the User object to calling the UserPreference::getUserDateTimePreferences() method in modules/UserPreferences/UserPreference.php
375 * @see UserPreference::getUserDateTimePreferences()
377 * @return array 'date' - date format for user ; 'time' - time format for user
379 public function getUserDateTimePreferences()
382 if ( func_num_args() > 0 ) {
383 $user = func_get_arg(0);
384 $GLOBALS['log']->deprecated('User::getUserDateTimePreferences() should not be used statically.');
389 return $user->_userPreferenceFocus->getUserDateTimePreferences();
393 * Interface for the User object to calling the UserPreference::loadPreferences() method in modules/UserPreferences/UserPreference.php
395 * @see UserPreference::loadPreferences()
397 * @param string $category name of the category to retreive, defaults to global scope
398 * @return bool successful?
400 public function loadPreferences(
405 if ( func_num_args() > 1 ) {
406 $user = func_get_arg(1);
407 $GLOBALS['log']->deprecated('User::loadPreferences() should not be used statically.');
412 return $user->_userPreferenceFocus->loadPreferences($category);
416 * Interface for the User object to calling the UserPreference::setPreference() method in modules/UserPreferences/UserPreference.php
418 * @see UserPreference::getPreference()
420 * @param string $name name of the preference to retreive
421 * @param string $category name of the category to retreive, defaults to global scope
422 * @return mixed the value of the preference (string, array, int etc)
424 public function getPreference(
430 if ( func_num_args() > 2 ) {
431 $user = func_get_arg(2);
432 $GLOBALS['log']->deprecated('User::getPreference() should not be used statically.');
437 return $user->_userPreferenceFocus->getPreference($name, $category);
440 function save($check_notify = false) {
442 $query = "SELECT count(id) as total from users WHERE status='Active' AND deleted=0 AND is_group=0 AND portal_only=0";
445 // wp: do not save user_preferences in this table, see user_preferences module
446 $this->user_preferences = '';
448 // if this is an admin user, do not allow is_group or portal_only flag to be set.
449 if ($this->is_admin) {
451 $this->portal_only = 0;
458 parent::save($check_notify);
462 $this->savePreferencesToDB();
467 * @return boolean true if the user is a member of the role_name, false otherwise
468 * @param string $role_name - Must be the exact name of the acl_role
469 * @param string $user_id - The user id to check for the role membership, empty string if current user
470 * @desc Determine whether or not a user is a member of an ACL Role. This function caches the
471 * results in the session or to prevent running queries after the first time executed.
472 * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc..
473 * All Rights Reserved..
474 * Contributor(s): ______________________________________..
476 function check_role_membership($role_name, $user_id = ''){
478 global $current_user;
481 $user_id = $current_user->id;
483 // Check the Sugar External Cache to see if this users memberships were cached
484 $role_array = sugar_cache_retrieve("RoleMemberships_".$user_id);
486 // If we are pulling the roles for the current user
487 if($user_id == $current_user->id){
488 // If the Session doesn't contain the values
489 if(!isset($_SESSION['role_memberships'])){
490 // This means the external cache already had it loaded
491 if(!empty($role_array))
492 $_SESSION['role_memberships'] = $role_array;
494 $_SESSION['role_memberships'] = ACLRole::getUserRoleNames($user_id);
495 $role_array = $_SESSION['role_memberships'];
498 // else the session had the values, so we assign to the role array
500 $role_array = $_SESSION['role_memberships'];
504 // If the external cache didn't contain the values, we get them and put them in cache
506 $role_array = ACLRole::getUserRoleNames($user_id);
507 sugar_cache_put("RoleMemberships_".$user_id, $role_array);
511 // If the role doesn't exist in the list of the user's roles
512 if(!empty($role_array) && in_array($role_name, $role_array))
518 function get_summary_text() {
519 //$this->_create_proper_name_field();
524 * @return string encrypted password for storage in DB and comparison against DB password.
525 * @param string $user_name - Must be non null and at least 2 characters
526 * @param string $user_password - Must be non null and at least 1 character.
527 * @desc Take an unencrypted username and password and return the encrypted password
528 * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc..
529 * All Rights Reserved..
530 * Contributor(s): ______________________________________..
532 function encrypt_password($user_password) {
533 // encrypt the password.
534 $salt = substr($this->user_name, 0, 2);
535 $encrypted_password = crypt($user_password, $salt);
537 return $encrypted_password;
541 * Authenicates the user; returns true if successful
546 public function authenticate_user(
550 $password = $GLOBALS['db']->quote($password);
551 $user_name = $GLOBALS['db']->quote($this->user_name);
552 $query = "SELECT * from $this->table_name where user_name='$user_name' AND user_hash='$password' AND (portal_only IS NULL OR portal_only !='1') AND (is_group IS NULL OR is_group !='1') ";
553 //$result = $this->db->requireSingleResult($query, false);
554 $result = $this->db->limitQuery($query,0,1,false);
555 $a = $this->db->fetchByAssoc($result);
556 // set the ID in the seed user. This can be used for retrieving the full user record later
558 // already logging this in load_user() method
559 //$GLOBALS['log']->fatal("SECURITY: failed login by $this->user_name");
562 $this->id = $a['id'];
568 * retrieves an User bean
569 * preformat name & full_name attribute with first/last
570 * loads User's preferences
572 * @param string id ID of the User
573 * @param bool encode encode the result
574 * @return object User bean
575 * @return null null if no User found
577 function retrieve($id, $encode = true) {
578 $ret = parent::retrieve($id, $encode);
580 if (isset ($_SESSION)) {
581 $this->loadPreferences();
587 function retrieve_by_email_address($email) {
589 $email1= strtoupper($email);
592 select id from users where id in ( SELECT er.bean_id AS id FROM email_addr_bean_rel er,
593 email_addresses ea WHERE ea.id = er.email_address_id
594 AND ea.deleted = 0 AND er.deleted = 0 AND er.bean_module = 'Users' AND email_address_caps IN ('{$email}') )
598 $res=$this->db->query($q);
599 $row=$this->db->fetchByAssoc($res);
601 if (!empty($row['id'])) {
602 return $this->retrieve($row['id']);
607 function bean_implements($interface) {
609 case 'ACL':return true;
616 * Load a user based on the user_name in $this
617 * @return -- this if load was successul and null if load failed.
618 * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc..
619 * All Rights Reserved..
620 * Contributor(s): ______________________________________..
622 function load_user($user_password) {
624 unset($GLOBALS['login_error']);
625 if(isset ($_SESSION['loginattempts'])) {
626 $_SESSION['loginattempts'] += 1;
628 $_SESSION['loginattempts'] = 1;
630 if($_SESSION['loginattempts'] > 5) {
631 $GLOBALS['log']->fatal('SECURITY: '.$this->user_name.' has attempted to login '.$_SESSION['loginattempts'].' times from IP address: '.$_SERVER['REMOTE_ADDR'].'.');
634 $GLOBALS['log']->debug("Starting user load for $this->user_name");
636 if (!isset ($this->user_name) || $this->user_name == "" || !isset ($user_password) || $user_password == "")
639 $user_hash = strtolower(md5($user_password));
640 if($this->authenticate_user($user_hash)) {
641 $query = "SELECT * from $this->table_name where id='$this->id'";
643 $GLOBALS['log']->fatal('SECURITY: User authentication for '.$this->user_name.' failed');
646 $r = $this->db->limitQuery($query, 0, 1, false);
647 $a = $this->db->fetchByAssoc($r);
648 if(empty($a) || !empty ($GLOBALS['login_error'])) {
649 $GLOBALS['log']->fatal('SECURITY: User authentication for '.$this->user_name.' failed - could not Load User from Database');
653 // Get the fields for the user
656 // If there is no user_hash is not present or is out of date, then create a new one.
657 if (!isset ($row['user_hash']) || $row['user_hash'] != $user_hash) {
658 $query = "UPDATE $this->table_name SET user_hash='$user_hash' where id='{$row['id']}'";
659 $this->db->query($query, true, "Error setting new hash for {$row['user_name']}: ");
662 // now fill in the fields.
663 foreach ($this->column_fields as $field) {
664 $GLOBALS['log']->info($field);
666 if (isset ($row[$field])) {
667 $GLOBALS['log']->info("=".$row[$field]);
669 $this-> $field = $row[$field];
673 $this->loadPreferences();
676 require_once ('modules/Versions/CheckVersions.php');
677 $invalid_versions = get_invalid_versions();
679 if (!empty ($invalid_versions)) {
680 if (isset ($invalid_versions['Rebuild Relationships'])) {
681 unset ($invalid_versions['Rebuild Relationships']);
683 // flag for pickup in DisplayWarnings.php
684 $_SESSION['rebuild_relationships'] = true;
687 if (isset ($invalid_versions['Rebuild Extensions'])) {
688 unset ($invalid_versions['Rebuild Extensions']);
690 // flag for pickup in DisplayWarnings.php
691 $_SESSION['rebuild_extensions'] = true;
694 $_SESSION['invalid_versions'] = $invalid_versions;
696 $this->fill_in_additional_detail_fields();
697 if ($this->status != "Inactive")
698 $this->authenticated = true;
700 unset ($_SESSION['loginattempts']);
705 * Verify that the current password is correct and write the new password to the DB.
707 * @param string $user name - Must be non null and at least 1 character.
708 * @param string $user_password - Must be non null and at least 1 character.
709 * @param string $new_password - Must be non null and at least 1 character.
710 * @return boolean - If passwords pass verification and query succeeds, return true, else return false.
712 function change_password(
715 $system_generated = '0'
719 global $current_user;
720 $GLOBALS['log']->debug("Starting password change for $this->user_name");
722 if (!isset ($new_password) || $new_password == "") {
723 $this->error_string = $mod_strings['ERR_PASSWORD_CHANGE_FAILED_1'].$current_user['user_name'].$mod_strings['ERR_PASSWORD_CHANGE_FAILED_2'];
727 $old_user_hash = strtolower(md5($user_password));
729 if (!is_admin($current_user) && !is_admin_for_module($current_user,'Users')) {
730 //check old password first
731 $query = "SELECT user_name FROM $this->table_name WHERE user_hash='$old_user_hash' AND id='$this->id'";
732 $result = $this->db->query($query, true);
733 $row = $this->db->fetchByAssoc($result);
734 $GLOBALS['log']->debug("select old password query: $query");
735 $GLOBALS['log']->debug("return result of $row");
737 $GLOBALS['log']->warn("Incorrect old password for ".$this->user_name."");
738 $this->error_string = $mod_strings['ERR_PASSWORD_INCORRECT_OLD_1'].$this->user_name.$mod_strings['ERR_PASSWORD_INCORRECT_OLD_2'];
743 $user_hash = strtolower(md5($new_password));
744 $this->setPreference('loginexpiration','0');
746 $now=date("Y-m-d H:i:s");
747 $query = "UPDATE $this->table_name SET user_hash='$user_hash', system_generated_password='$system_generated', pwd_last_changed='$now' where id='$this->id'";
748 $this->db->query($query, true, "Error setting new password for $this->user_name: ");
749 $_SESSION['hasExpiredPassword'] = '0';
753 function is_authenticated() {
754 return $this->authenticated;
757 function fill_in_additional_list_fields() {
758 $this->fill_in_additional_detail_fields();
761 function fill_in_additional_detail_fields() {
764 $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";
765 $result = $this->db->query($query, true, "Error filling in additional detail fields");
767 $row = $this->db->fetchByAssoc($result);
768 $GLOBALS['log']->debug("additional detail query results: $row");
771 $this->reports_to_name = stripslashes($row['first_name'].' '.$row['last_name']);
773 $this->reports_to_name = '';
776 $this->_create_proper_name_field();
779 public function retrieve_user_id(
783 $userFocus = new User;
784 $userFocus->retrieve_by_string_fields(array('user_name'=>$user_name));
785 if ( empty($userFocus->id) )
788 return $userFocus->id;
792 * @return -- returns a list of all users in the system.
793 * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc..
794 * All Rights Reserved..
795 * Contributor(s): ______________________________________..
797 function verify_data($ieVerified=true) {
798 global $mod_strings, $current_user;
801 if (!empty ($this->id)) {
802 // Make sure the user doesn't report to themselves.
803 $reports_to_self = 0;
804 $check_user = $this->reports_to_id;
805 $already_seen_list = array ();
806 while (!empty ($check_user)) {
807 if (isset ($already_seen_list[$check_user])) {
808 // This user doesn't actually report to themselves
809 // But someone above them does.
810 $reports_to_self = 1;
813 if ($check_user == $this->id) {
814 $reports_to_self = 1;
817 $already_seen_list[$check_user] = 1;
818 $query = "SELECT reports_to_id FROM users WHERE id='".$this->db->quote($check_user)."'";
819 $result = $this->db->query($query, true, "Error checking for reporting-loop");
820 $row = $this->db->fetchByAssoc($result);
821 echo ("fetched: ".$row['reports_to_id']." from ".$check_user."<br>");
822 $check_user = $row['reports_to_id'];
825 if ($reports_to_self == 1) {
826 $this->error_string .= $mod_strings['ERR_REPORT_LOOP'];
831 $query = "SELECT user_name from users where user_name='$this->user_name' AND deleted=0";
832 if(!empty($this->id))$query .= " AND id<>'$this->id'";
833 $result = $this->db->query($query, true, "Error selecting possible duplicate users: ");
834 $dup_users = $this->db->fetchByAssoc($result);
836 if (!empty($dup_users)) {
837 $this->error_string .= $mod_strings['ERR_USER_NAME_EXISTS_1'].$this->user_name.$mod_strings['ERR_USER_NAME_EXISTS_2'];
841 if (($current_user->is_admin == "on")) {
842 if($this->db->dbType == 'mssql'){
843 $query = "SELECT user_name from users where is_admin = 1 AND deleted=0";
845 $query = "SELECT user_name from users where is_admin = 'on' AND deleted=0";
847 $result = $this->db->query($query, true, "Error selecting possible duplicate users: ");
848 $remaining_admins = $this->db->getRowCount($result);
850 if (($remaining_admins <= 1) && ($this->is_admin != "on") && ($this->id == $current_user->id)) {
851 $GLOBALS['log']->debug("Number of remaining administrator accounts: {$remaining_admins}");
852 $this->error_string .= $mod_strings['ERR_LAST_ADMIN_1'].$this->user_name.$mod_strings['ERR_LAST_ADMIN_2'];
856 ///////////////////////////////////////////////////////////////////////
857 //// InboundEmail verification failure
860 $this->error_string .= '<br />'.$mod_strings['ERR_EMAIL_NO_OPTS'];
866 function get_list_view_data() {
868 global $current_user;
870 $user_fields = $this->get_list_view_array();
872 $user_fields['IS_ADMIN_IMAGE'] = SugarThemeRegistry::current()->getImage('check_inline', '');
873 elseif (!$this->is_admin) $user_fields['IS_ADMIN'] = '';
875 $user_fields['IS_GROUP_IMAGE'] = SugarThemeRegistry::current()->getImage('check_inline', '');
877 $user_fields['IS_GROUP_IMAGE'] = '';
878 $user_fields['NAME'] = empty ($this->name) ? '' : $this->name;
880 $user_fields['REPORTS_TO_NAME'] = $this->reports_to_name;
882 $user_fields['EMAIL1'] = $this->emailAddress->getPrimaryAddress($this);
887 function list_view_parse_additional_sections(& $list_form, $xTemplateSection) {
891 function save_relationship_changes($is_update) {
897 function create_export_query($order_by, $where) {
898 include('modules/Users/field_arrays.php');
901 foreach($fields_array['User']['export_fields'] as $field) {
902 $cols .= (empty($cols)) ? '' : ', ';
906 $query = "SELECT {$cols} FROM users ";
908 $where_auto = " users.deleted = 0";
911 $query .= " WHERE $where AND ".$where_auto;
913 $query .= " WHERE ".$where_auto;
915 // admin for module user is not be able to export a super-admin
916 global $current_user;
917 if(!$current_user->is_admin){
918 $query .= " AND users.is_admin=0";
922 $query .= " ORDER BY $order_by";
924 $query .= " ORDER BY users.user_name";
929 /** Returns a list of the associated users
930 * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc..
931 * All Rights Reserved..
932 * Contributor(s): ______________________________________..
934 function get_meetings() {
935 // First, get the list of IDs.
936 $query = "SELECT meeting_id as id from meetings_users where user_id='$this->id' AND deleted=0";
937 return $this->build_related_list($query, new Meeting());
939 function get_calls() {
940 // First, get the list of IDs.
941 $query = "SELECT call_id as id from calls_users where user_id='$this->id' AND deleted=0";
942 return $this->build_related_list($query, new Call());
946 * generates Javascript to display I-E mail counts, both personal and group
948 function displayEmailCounts() {
950 $new = translate('LBL_NEW', 'Emails');
951 $default = 'index.php?module=Emails&action=ListView&assigned_user_id='.$this->id;
953 $verts = array('Love', 'Links', 'Pipeline', 'RipCurl', 'SugarLite');
955 if($this->hasPersonalEmail()) {
956 $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\'');
957 $a = $this->db->fetchByAssoc($r);
958 if(in_array($theme, $verts)) {
961 $count .= ' ';
963 $count .= '<a href='.$default.'&type=inbound>'.translate('LBL_LIST_TITLE_MY_INBOX', 'Emails').': ('.$a['c'].' '.$new.')</a>';
965 if(!in_array($theme, $verts)) {
970 $r = $this->db->query('SELECT id FROM users WHERE users.is_group = 1 AND deleted = 0');
973 while($a = $this->db->fetchByAssoc($r)) {
974 if($groupIds != '') {$groupIds .= ', ';}
975 $groupIds .= "'".$a['id']."'";
979 if(strlen($groupIds) > 0) {
980 $groupQuery = 'SELECT count(*) AS c FROM emails ';
981 $groupQuery .= ' WHERE emails.deleted=0 AND emails.assigned_user_id IN ('.$groupIds.') AND emails.type = \'inbound\' AND emails.status = \'unread\'';
982 $r = $this->db->query($groupQuery);
983 if(is_resource($r)) {
984 $a = $this->db->fetchByAssoc($r);
990 if(in_array($theme, $verts)) $count .= '<br />';
991 if(empty($count)) $count .= ' ';
992 $count .= '<a href=index.php?module=Emails&action=ListViewGroup>'.translate('LBL_LIST_TITLE_GROUP_INBOX', 'Emails').': ('.$total.' '.$new.')</a>';
994 $out = '<script type="text/javascript" language="Javascript">';
995 $out .= 'var welcome = document.getElementById("welcome");';
996 $out .= 'var welcomeContent = welcome.innerHTML;';
997 $out .= 'welcome.innerHTML = welcomeContent + "'.$count.'";';
1003 function getPreferredEmail() {
1005 $nameEmail = $this->getUsersNameAndEmail();
1006 $prefAddr = $nameEmail['email'];
1007 $fullName = $nameEmail['name'];
1008 if (empty ($prefAddr)) {
1009 $nameEmail = $this->getSystemDefaultNameAndEmail();
1010 $prefAddr = $nameEmail['email'];
1011 $fullName = $nameEmail['name'];
1013 $fullName = from_html($fullName);
1014 $ret['name'] = $fullName;
1015 $ret['email'] = $prefAddr;
1019 function getUsersNameAndEmail() {
1022 if(!empty($this->salutation)) $salutation = $this->salutation;
1024 if(!empty($this->first_name)) {
1025 $fullName = trim($salutation.' '.$this->first_name.' '.$this->last_name);
1026 } elseif(!empty($this->name)) {
1027 $fullName = $this->name;
1029 $prefAddr = $this->emailAddress->getPrimaryAddress($this);
1031 if (empty ($prefAddr)) {
1032 $prefAddr = $this->emailAddress->getReplyToAddress($this);
1034 return array('email' => $prefAddr , 'name' => $fullName);
1038 function getSystemDefaultNameAndEmail() {
1040 $email = new Email();
1041 $return = $email->getSystemDefaultEmail();
1042 $prefAddr = $return['email'];
1043 $fullName = $return['name'];
1044 return array('email' => $prefAddr , 'name' => $fullName);
1048 * sets User email default in config.php if not already set by install - i.
1051 function setDefaultsInConfig() {
1052 global $sugar_config;
1053 $sugar_config['email_default_client'] = 'sugar';
1054 $sugar_config['email_default_editor'] = 'html';
1055 ksort($sugar_config);
1056 write_array_to_file('sugar_config', $sugar_config, 'config.php');
1057 return $sugar_config;
1061 * returns User's email address based on descending order of preferences
1063 * @param string id GUID of target user if needed
1064 * @return array Assoc array for an email and name
1066 function getEmailInfo($id='') {
1070 $user->retrieve($id);
1074 $fromName = $user->getPreference('mail_fromname');
1075 if(empty($fromName)) {
1076 // cn: bug 8586 - localized name format
1077 $fromName = $user->full_name;
1081 $fromaddr = $user->getPreference('mail_fromaddress');
1082 if(empty($fromaddr)) {
1083 if(!empty($user->email1) && isset($user->email1)) {
1084 $fromaddr = $user->email1;
1085 } elseif(!empty($user->email2) && isset($user->email2)) {
1086 $fromaddr = $user->email2;
1088 $r = $user->db->query("SELECT value FROM config WHERE name = 'fromaddress'");
1089 $a = $user->db->fetchByAssoc($r);
1090 $fromddr = $a['value'];
1094 $ret['name'] = $fromName;
1095 $ret['email'] = $fromaddr;
1101 * returns opening <a href=xxxx for a contact, account, etc
1102 * cascades from User set preference to System-wide default
1103 * @return string link
1104 * @param attribute the email addy
1105 * @param focus the parent bean
1107 * @param return_module
1108 * @param return_action
1112 function getEmailLink2($emailAddress, &$focus, $contact_id='', $ret_module='', $ret_action='DetailView', $ret_id='', $class='') {
1114 global $sugar_config;
1116 if(!isset($sugar_config['email_default_client'])) {
1117 $this->setDefaultsInConfig();
1120 $userPref = $this->getPreference('email_link_type');
1121 $defaultPref = $sugar_config['email_default_client'];
1122 if($userPref != '') {
1123 $client = $userPref;
1125 $client = $defaultPref;
1128 if($client == 'sugar') {
1133 $to_addrs_names = '';
1134 $to_addrs_emails = '';
1136 if(!empty($focus->salutation)) $salutation = $focus->salutation;
1138 if(!empty($focus->first_name)) {
1139 $fullName = trim($salutation.' '.$focus->first_name.' '.$focus->last_name);
1140 } elseif(!empty($focus->name)) {
1141 $fullName = $focus->name;
1144 if(empty($ret_module)) $ret_module = $focus->module_dir;
1145 if(empty($ret_id)) $ret_id = $focus->id;
1146 if($focus->object_name == 'Contact') {
1147 $contact_id = $focus->id;
1148 $to_addrs_ids = $focus->id;
1149 $to_addrs_names = $fullName;
1150 $to_addrs_emails = $focus->email1;
1153 $emailLinkUrl = 'contact_id='.$contact_id.
1154 '&parent_type='.$focus->module_dir.
1155 '&parent_id='.$focus->id.
1156 '&parent_name='.urlencode($fullName).
1157 '&to_addrs_ids='.$to_addrs_ids.
1158 '&to_addrs_names='.urlencode($to_addrs_names).
1159 '&to_addrs_emails='.urlencode($to_addrs_emails).
1160 '&to_email_addrs='.urlencode($fullName . ' <' . $emailAddress . '>').
1161 '&return_module='.$ret_module.
1162 '&return_action='.$ret_action.
1163 '&return_id='.$ret_id;
1165 //Generate the compose package for the quick create options.
1166 //$json = getJSONobj();
1167 //$composeOptionsLink = $json->encode( array('composeOptionsLink' => $emailLinkUrl,'id' => $focus->id) );
1168 require_once('modules/Emails/EmailUI.php');
1169 $eUi = new EmailUI();
1170 $j_quickComposeOptions = $eUi->generateComposePackageForQuickCreateFromComposeUrl($emailLinkUrl);
1172 $emailLink = "<a href='javascript:void(0);' onclick='SUGAR.quickCompose.init($j_quickComposeOptions);' class='$class'>";
1176 $emailLink = '<a href="mailto:'.$emailAddress.'" class="'.$class.'">';
1183 * returns opening <a href=xxxx for a contact, account, etc
1184 * cascades from User set preference to System-wide default
1185 * @return string link
1186 * @param attribute the email addy
1187 * @param focus the parent bean
1189 * @param return_module
1190 * @param return_action
1194 function getEmailLink($attribute, &$focus, $contact_id='', $ret_module='', $ret_action='DetailView', $ret_id='', $class='') {
1196 global $sugar_config;
1198 if(!isset($sugar_config['email_default_client'])) {
1199 $this->setDefaultsInConfig();
1202 $userPref = $this->getPreference('email_link_type');
1203 $defaultPref = $sugar_config['email_default_client'];
1204 if($userPref != '') {
1205 $client = $userPref;
1207 $client = $defaultPref;
1210 if($client == 'sugar') {
1215 $to_addrs_names = '';
1216 $to_addrs_emails = '';
1218 if(!empty($focus->salutation)) $salutation = $focus->salutation;
1220 if(!empty($focus->first_name)) {
1221 $fullName = trim($salutation.' '.$focus->first_name.' '.$focus->last_name);
1222 } elseif(!empty($focus->name)) {
1223 $fullName = $focus->name;
1225 if(!empty($focus->$attribute)) {
1226 $email = $focus->$attribute;
1230 if(empty($ret_module)) $ret_module = $focus->module_dir;
1231 if(empty($ret_id)) $ret_id = $focus->id;
1232 if($focus->object_name == 'Contact') {
1233 $contact_id = $focus->id;
1234 $to_addrs_ids = $focus->id;
1235 $to_addrs_names = $fullName;
1236 $to_addrs_emails = $focus->email1;
1239 $emailLinkUrl = 'contact_id='.$contact_id.
1240 '&parent_type='.$focus->module_dir.
1241 '&parent_id='.$focus->id.
1242 '&parent_name='.urlencode($fullName).
1243 '&to_addrs_ids='.$to_addrs_ids.
1244 '&to_addrs_names='.urlencode($to_addrs_names).
1245 '&to_addrs_emails='.urlencode($to_addrs_emails).
1246 '&to_email_addrs='.urlencode($fullName . ' <' . $email . '>').
1247 '&return_module='.$ret_module.
1248 '&return_action='.$ret_action.
1249 '&return_id='.$ret_id;
1251 //Generate the compose package for the quick create options.
1252 require_once('modules/Emails/EmailUI.php');
1253 $eUi = new EmailUI();
1254 $j_quickComposeOptions = $eUi->generateComposePackageForQuickCreateFromComposeUrl($emailLinkUrl);
1255 $emailLink = "<a href='javascript:void(0);' onclick='SUGAR.quickCompose.init($j_quickComposeOptions);' class='$class'>";
1259 $emailLink = '<a href="mailto:'.$focus->$attribute.'" class="'.$class.'">';
1267 * gets a human-readable explanation of the format macro
1268 * @return string Human readable name format
1270 function getLocaleFormatDesc() {
1272 global $mod_strings;
1273 global $app_strings;
1275 $format['f'] = $mod_strings['LBL_LOCALE_DESC_FIRST'];
1276 $format['l'] = $mod_strings['LBL_LOCALE_DESC_LAST'];
1277 $format['s'] = $mod_strings['LBL_LOCALE_DESC_SALUTATION'];
1278 $format['t'] = $mod_strings['LBL_LOCALE_DESC_TITLE'];
1280 $name['f'] = $app_strings['LBL_LOCALE_NAME_EXAMPLE_FIRST'];
1281 $name['l'] = $app_strings['LBL_LOCALE_NAME_EXAMPLE_LAST'];
1282 $name['s'] = $app_strings['LBL_LOCALE_NAME_EXAMPLE_SALUTATION'];
1283 $name['t'] = $app_strings['LBL_LOCALE_NAME_EXAMPLE_TITLE'];
1285 $macro = $locale->getLocaleFormatMacro();
1289 for($i=0; $i<strlen($macro); $i++) {
1290 if(array_key_exists($macro{$i}, $format)) {
1291 $ret1 .= "<i>".$format[$macro{$i}]."</i>";
1292 $ret2 .= "<i>".$name[$macro{$i}]."</i>";
1294 $ret1 .= $macro{$i};
1295 $ret2 .= $macro{$i};
1298 return $ret1."<br />".$ret2;
1303 * Whether or not based on the user's locale if we should show the last name first.
1307 public function showLastNameFirst(){
1309 $localeFormat = $locale->getLocaleFormatMacro($this);
1310 if ( strpos($localeFormat,'l') > strpos($localeFormat,'f') ) {
1319 function create_new_list_query($order_by, $where,$filter=array(),$params=array(), $show_deleted = 0,$join_type='', $return_array = false,$parentbean=null, $singleSelect = false)
1320 { //call parent method, specifying for array to be returned
1321 $ret_array = parent::create_new_list_query($order_by, $where,$filter,$params, $show_deleted,$join_type, true,$parentbean, $singleSelect);
1323 //if this is being called from webservices, then run additional code
1324 if(!empty($GLOBALS['soap_server_object'])){
1326 //if this is a single select, then secondary queries are being run that may result in duplicate rows being returned through the
1327 //left joins with meetings/tasks/call. Add a group by to return one user record (bug 40250)
1330 $ret_array['order_by'] = ' Group By '.$this->table_name.'.id '.$ret_array['order_by'];
1334 //return array or query string
1340 return $ret_array['select'] . $ret_array['from'] . $ret_array['where']. $ret_array['order_by'];