]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - modules/Users/User.php
Release 6.1.5
[Github/sugarcrm.git] / modules / Users / User.php
1 <?php
2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4  * SugarCRM is a customer relationship management program developed by
5  * SugarCRM, Inc. Copyright (C) 2004-2011 SugarCRM Inc.
6  * 
7  * This program is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU Affero General Public License version 3 as published by the
9  * Free Software Foundation with the addition of the following permission added
10  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11  * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
13  * 
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
17  * details.
18  * 
19  * You should have received a copy of the GNU Affero General Public License along with
20  * this program; if not, see http://www.gnu.org/licenses or write to the Free
21  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22  * 02110-1301 USA.
23  * 
24  * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25  * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
26  * 
27  * The interactive user interfaces in modified source and object code versions
28  * of this program must display Appropriate Legal Notices, as required under
29  * Section 5 of the GNU Affero General Public License version 3.
30  * 
31  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32  * these Appropriate Legal Notices must retain the display of the "Powered by
33  * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34  * technical reasons, the Appropriate Legal Notices must display the words
35  * "Powered by SugarCRM".
36  ********************************************************************************/
37
38 /*********************************************************************************
39
40  * Description: TODO:  To be written.
41  * Portions created by SugarCRM are Copyright (C) SugarCRM, Inc.
42  * All Rights Reserved.
43  * Contributor(s): ______________________________________..
44  ********************************************************************************/
45
46 require_once('include/SugarObjects/templates/person/Person.php');
47
48
49 // User is used to store customer information.
50 class User extends Person {
51         // Stored fields
52         var $name = '';
53         var $full_name;
54         var $id;
55         var $user_name;
56         var $user_hash;
57         var $salutation;
58         var $first_name;
59         var $last_name;
60         var $date_entered;
61         var $date_modified;
62         var $modified_user_id;
63         var $created_by;
64         var $created_by_name;
65         var $modified_by_name;
66         var $description;
67         var $phone_home;
68         var $phone_mobile;
69         var $phone_work;
70         var $phone_other;
71         var $phone_fax;
72         var $email1;
73         var $email2;
74         var $address_street;
75         var $address_city;
76         var $address_state;
77         var $address_postalcode;
78         var $address_country;
79         var $status;
80         var $title;
81         var $portal_only;
82         var $department;
83         var $authenticated = false;
84         var $error_string;
85         var $is_admin;
86         var $employee_status;
87         var $messenger_id;
88         var $messenger_type;
89         var $is_group;
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
92         var $team_id;
93
94         var $receive_notifications;
95
96         var $reports_to_name;
97         var $reports_to_id;
98         var $team_exists = false;
99         var $table_name = "users";
100         var $module_dir = 'Users';
101         var $object_name = "User";
102         var $user_preferences;
103
104         var $importable = true;
105         var $_userPreferenceFocus;
106
107         var $encodeFields = Array ("first_name", "last_name", "description");
108
109         // This is used to retrieve related fields from form posts.
110         var $additional_column_fields = array ('reports_to_name'
111         );
112
113         var $emailAddress;
114
115
116         var $new_schema = true;
117
118         function User() {
119                 parent::Person();
120
121                 $this->_loadUserPreferencesFocus();
122         }
123
124         protected function _loadUserPreferencesFocus()
125         {
126             $this->_userPreferenceFocus = new UserPreference($this);
127         }
128
129     /**
130      * returns an admin user
131      */
132     public function getSystemUser()
133     {
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',
138                 'is_admin' => '1',
139                 ));
140
141         return $this;
142     }
143
144
145         /**
146          * convenience function to get user's default signature
147          */
148         function getDefaultSignature() {
149                 if($defaultId = $this->getPreference('signature_default')) {
150                         return $this->getSignature($defaultId);
151                 } else {
152                         return array();
153                 }
154         }
155
156         /**
157          * retrieves the signatures for a user
158          * @param string id ID of user_signature
159          * @return array ID, signature, and signature_html
160          */
161         public function getSignature($id)
162         {
163             $signatures = $this->getSignaturesArray();
164
165             return $signatures[$id];
166         }
167
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);
171
172                 // provide "none"
173                 $sig = array(""=>"");
174
175                 while($a = $this->db->fetchByAssoc($r)) {
176                         $sig[$a['id']] = $a;
177                 }
178
179                 return $sig;
180         }
181
182         /**
183          * retrieves any signatures that the User may have created as <select>
184          */
185         public function getSignatures(
186             $live = false,
187             $defaultSig = '',
188             $forSettings = false
189             )
190         {
191                 $sig = $this->getSignaturesArray();
192                 $sigs = array();
193                 foreach ($sig as $key => $arr)
194                 {
195                         $sigs[$key] = !empty($arr['name']) ? $arr['name'] : '';
196                 }
197
198                 $change = '';
199                 if(!$live) {
200                         $change = ($forSettings) ? "onChange='displaySignatureEdit();'" : "onChange='setSigEditButtonVisibility();'";
201                 }
202
203                 $id = (!$forSettings) ? 'signature_id' : 'signature_idDisplay';
204
205                 $out  = "<select {$change} id='{$id}' name='{$id}'>";
206                 $out .= get_select_options_with_id($sigs, $defaultSig).'</select>';
207
208                 return $out;
209         }
210
211         /**
212          * returns buttons and JS for signatures
213          */
214         function getSignatureButtons($jscall='', $defaultDisplay=false) {
215                 global $mod_strings;
216
217                 $jscall = empty($jscall) ? 'open_email_signature_form' : $jscall;
218
219                 $butts  = "<input class='button' onclick='javascript:{$jscall}(\"\", \"{$this->id}\");' value='{$mod_strings['LBL_BUTTON_CREATE']}' type='button'>&nbsp;";
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">&nbsp;
222                                         </span>';
223                 } else {
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">&nbsp;
225                                         </span>';
226                 }
227                 return $butts;
228         }
229
230         /**
231          * performs a rudimentary check to verify if a given user has setup personal
232          * InboundEmail
233          *
234          * @return bool
235          */
236         public function hasPersonalEmail()
237         {
238             $focus = new InboundEmail;
239             $focus->retrieve_by_string_fields(array('group_id' => $this->id));
240
241             return !empty($focus->id);
242         }
243
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.
247          */
248         function getUserPrivGuid() {
249         $userPrivGuid = $this->getPreference('userPrivGuid', 'global', $this);
250                 if ($userPrivGuid) {
251                         return $userPrivGuid;
252                 } else {
253                         $this->setUserPrivGuid();
254                         if (!isset ($_SESSION['setPrivGuid'])) {
255                                 $_SESSION['setPrivGuid'] = true;
256                                 $userPrivGuid = $this->getUserPrivGuid();
257                                 return $userPrivGuid;
258                         } else {
259                                 sugar_die("Breaking Infinite Loop Condition: Could not setUserPrivGuid.");
260                         }
261                 }
262         }
263
264         function setUserPrivGuid() {
265                 $privGuid = create_guid();
266                 //($name, $value, $nosession=0)
267                 $this->setPreference('userPrivGuid', $privGuid, 0, 'global', $this);
268         }
269
270         /**
271          * Interface for the User object to calling the UserPreference::setPreference() method in modules/UserPreferences/UserPreference.php
272          *
273          * @see UserPreference::setPreference()
274          *
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
279          */
280         public function setPreference(
281             $name,
282             $value,
283             $nosession = 0,
284             $category = 'global'
285             )
286         {
287             // for BC
288             if ( func_num_args() > 4 ) {
289                 $user = func_get_arg(4);
290                 $GLOBALS['log']->deprecated('User::setPreferences() should not be used statically.');
291             }
292             else
293                 $user = $this;
294
295         $user->_userPreferenceFocus->setPreference($name, $value, $category);
296         }
297
298         /**
299          * Interface for the User object to calling the UserPreference::resetPreferences() method in modules/UserPreferences/UserPreference.php
300          *
301          * @see UserPreference::resetPreferences()
302          *
303          * @param string $category category to reset
304          */
305         public function resetPreferences(
306             $category = null
307             )
308         {
309             // for BC
310             if ( func_num_args() > 1 ) {
311                 $user = func_get_arg(1);
312                 $GLOBALS['log']->deprecated('User::resetPreferences() should not be used statically.');
313             }
314             else
315                 $user = $this;
316
317         $user->_userPreferenceFocus->resetPreferences($category);
318         }
319
320
321         /**
322          * Interface for the User object to calling the UserPreference::isPreferenceSizeTooLarge() method in modules/UserPreferences/UserPreference.php
323          *
324          * @see UserPreference::isPreferenceSizeTooLarge()
325          *
326          * @param string $category category to check
327          */
328         public function isPreferenceSizeTooLarge(
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::resetPreferences() should not be used statically.');
336             }
337             else
338                 $user = $this;
339
340         return $user->_userPreferenceFocus->isPreferenceSizeTooLarge($category);
341         }
342         
343         
344         /**
345          * Interface for the User object to calling the UserPreference::savePreferencesToDB() method in modules/UserPreferences/UserPreference.php
346          *
347          * @see UserPreference::savePreferencesToDB()
348          */
349         public function savePreferencesToDB()
350         {
351         // for BC
352             if ( func_num_args() > 0 ) {
353                 $user = func_get_arg(0);
354                 $GLOBALS['log']->deprecated('User::savePreferencesToDB() should not be used statically.');
355             }
356             else
357                 $user = $this;
358
359         $user->_userPreferenceFocus->savePreferencesToDB();
360         }
361
362         /**
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?
366          */
367         public function reloadPreferences($category = 'global')
368         {
369             return $this->_userPreferenceFocus->reloadPreferences($category = 'global');
370         }
371
372         /**
373          * Interface for the User object to calling the UserPreference::getUserDateTimePreferences() method in modules/UserPreferences/UserPreference.php
374          *
375          * @see UserPreference::getUserDateTimePreferences()
376          *
377          * @return array 'date' - date format for user ; 'time' - time format for user
378          */
379         public function getUserDateTimePreferences()
380         {
381         // for BC
382             if ( func_num_args() > 0 ) {
383                 $user = func_get_arg(0);
384                 $GLOBALS['log']->deprecated('User::getUserDateTimePreferences() should not be used statically.');
385             }
386             else
387                 $user = $this;
388
389         return $user->_userPreferenceFocus->getUserDateTimePreferences();
390         }
391
392         /**
393          * Interface for the User object to calling the UserPreference::loadPreferences() method in modules/UserPreferences/UserPreference.php
394          *
395          * @see UserPreference::loadPreferences()
396          *
397          * @param string $category name of the category to retreive, defaults to global scope
398          * @return bool successful?
399          */
400         public function loadPreferences(
401             $category = 'global'
402             )
403         {
404             // for BC
405             if ( func_num_args() > 1 ) {
406                 $user = func_get_arg(1);
407                 $GLOBALS['log']->deprecated('User::loadPreferences() should not be used statically.');
408             }
409             else
410                 $user = $this;
411
412         return $user->_userPreferenceFocus->loadPreferences($category);
413         }
414
415         /**
416          * Interface for the User object to calling the UserPreference::setPreference() method in modules/UserPreferences/UserPreference.php
417          *
418          * @see UserPreference::getPreference()
419          *
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)
423          */
424         public function getPreference(
425             $name,
426             $category = 'global'
427             )
428         {
429             // for BC
430             if ( func_num_args() > 2 ) {
431                 $user = func_get_arg(2);
432                 $GLOBALS['log']->deprecated('User::getPreference() should not be used statically.');
433             }
434             else
435                 $user = $this;
436
437         return $user->_userPreferenceFocus->getPreference($name, $category);
438         }
439
440         function save($check_notify = false) {
441
442                 $query = "SELECT count(id) as total from users WHERE status='Active' AND deleted=0 AND is_group=0 AND portal_only=0";
443
444
445                 // wp: do not save user_preferences in this table, see user_preferences module
446                 $this->user_preferences = '';
447
448                 // if this is an admin user, do not allow is_group or portal_only flag to be set.
449                 if ($this->is_admin) {
450                         $this->is_group = 0;
451                         $this->portal_only = 0;
452                 }
453
454
455
456
457
458                 parent::save($check_notify);
459
460
461
462         $this->savePreferencesToDB();
463         return $this->id;
464         }
465
466         /**
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): ______________________________________..
475         */
476         function check_role_membership($role_name, $user_id = ''){
477
478                 global $current_user;
479
480                 if(empty($user_id))
481                         $user_id = $current_user->id;
482
483                 // Check the Sugar External Cache to see if this users memberships were cached
484                 $role_array = sugar_cache_retrieve("RoleMemberships_".$user_id);
485
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;
493                                 else{
494                                         $_SESSION['role_memberships'] = ACLRole::getUserRoleNames($user_id);
495                                         $role_array = $_SESSION['role_memberships'];
496                                 }
497                         }
498                         // else the session had the values, so we assign to the role array
499                         else{
500                                 $role_array = $_SESSION['role_memberships'];
501                         }
502                 }
503                 else{
504                         // If the external cache didn't contain the values, we get them and put them in cache
505                         if(!$role_array){
506                                 $role_array = ACLRole::getUserRoleNames($user_id);
507                                 sugar_cache_put("RoleMemberships_".$user_id, $role_array);
508                         }
509                 }
510
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))
513                         return true;
514                 else
515                         return false;
516         }
517
518     function get_summary_text() {
519         //$this->_create_proper_name_field();
520         return $this->name;
521         }
522
523         /**
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): ______________________________________..
531         */
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);
536
537                 return $encrypted_password;
538         }
539
540         /**
541          * Authenicates the user; returns true if successful
542          *
543          * @param $password
544          * @return bool
545          */
546         public function authenticate_user(
547             $password
548             )
549         {
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
557                 if (empty ($a)) {
558                         // already logging this in load_user() method
559                         //$GLOBALS['log']->fatal("SECURITY: failed login by $this->user_name");
560                         return false;
561                 } else {
562                         $this->id = $a['id'];
563                         return true;
564                 }
565         }
566
567     /**
568      * retrieves an User bean
569      * preformat name & full_name attribute with first/last
570      * loads User's preferences
571      *
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
576      */
577         function retrieve($id, $encode = true) {
578                 $ret = parent::retrieve($id, $encode);
579                 if ($ret) {
580                         if (isset ($_SESSION)) {
581                                 $this->loadPreferences();
582                         }
583                 }
584                 return $ret;
585         }
586
587         function retrieve_by_email_address($email) {
588
589                 $email1= strtoupper($email);
590                 $q=<<<EOQ
591
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}') )
595 EOQ;
596
597
598                 $res=$this->db->query($q);
599                 $row=$this->db->fetchByAssoc($res);
600
601                 if (!empty($row['id'])) {
602                         return $this->retrieve($row['id']);
603                 }
604                 return '';
605         }
606
607    function bean_implements($interface) {
608         switch($interface){
609             case 'ACL':return true;
610         }
611         return false;
612     }
613
614
615         /**
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): ______________________________________..
621          */
622         function load_user($user_password) {
623                 global $login_error;
624                 unset($GLOBALS['login_error']);
625                 if(isset ($_SESSION['loginattempts'])) {
626                         $_SESSION['loginattempts'] += 1;
627                 } else {
628                         $_SESSION['loginattempts'] = 1;
629                 }
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'].'.');
632                 }
633
634                 $GLOBALS['log']->debug("Starting user load for $this->user_name");
635
636                 if (!isset ($this->user_name) || $this->user_name == "" || !isset ($user_password) || $user_password == "")
637                         return null;
638
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'";
642                 } else {
643                         $GLOBALS['log']->fatal('SECURITY: User authentication for '.$this->user_name.' failed');
644                         return null;
645                 }
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');
650                         return null;
651                 }
652
653                 // Get the fields for the user
654                 $row = $a;
655
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']}: ");
660                 }
661
662                 // now fill in the fields.
663                 foreach ($this->column_fields as $field) {
664                         $GLOBALS['log']->info($field);
665
666                         if (isset ($row[$field])) {
667                                 $GLOBALS['log']->info("=".$row[$field]);
668
669                                 $this-> $field = $row[$field];
670                         }
671                 }
672
673                 $this->loadPreferences();
674
675
676                 require_once ('modules/Versions/CheckVersions.php');
677                 $invalid_versions = get_invalid_versions();
678
679                 if (!empty ($invalid_versions)) {
680                         if (isset ($invalid_versions['Rebuild Relationships'])) {
681                                 unset ($invalid_versions['Rebuild Relationships']);
682
683                                 // flag for pickup in DisplayWarnings.php
684                                 $_SESSION['rebuild_relationships'] = true;
685                         }
686
687                         if (isset ($invalid_versions['Rebuild Extensions'])) {
688                                 unset ($invalid_versions['Rebuild Extensions']);
689
690                                 // flag for pickup in DisplayWarnings.php
691                                 $_SESSION['rebuild_extensions'] = true;
692                         }
693
694                         $_SESSION['invalid_versions'] = $invalid_versions;
695                 }
696                 $this->fill_in_additional_detail_fields();
697                 if ($this->status != "Inactive")
698                         $this->authenticated = true;
699
700                 unset ($_SESSION['loginattempts']);
701                 return $this;
702         }
703
704         /**
705          * Verify that the current password is correct and write the new password to the DB.
706          *
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.
711          */
712         function change_password(
713             $user_password,
714             $new_password,
715             $system_generated = '0'
716             )
717         {
718             global $mod_strings;
719                 global $current_user;
720                 $GLOBALS['log']->debug("Starting password change for $this->user_name");
721
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'];
724                         return false;
725                 }
726
727                 $old_user_hash = strtolower(md5($user_password));
728
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");
736             if ($row == null) {
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'];
739                                 return false;
740                         }
741                 }
742
743         $user_hash = strtolower(md5($new_password));
744         $this->setPreference('loginexpiration','0');
745         //set new password
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';
750                 return true;
751         }
752
753         function is_authenticated() {
754                 return $this->authenticated;
755         }
756
757         function fill_in_additional_list_fields() {
758                 $this->fill_in_additional_detail_fields();
759         }
760
761         function fill_in_additional_detail_fields() {
762                 global $locale;
763
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");
766
767                 $row = $this->db->fetchByAssoc($result);
768                 $GLOBALS['log']->debug("additional detail query results: $row");
769
770                 if ($row != null) {
771                         $this->reports_to_name = stripslashes($row['first_name'].' '.$row['last_name']);
772                 } else {
773                         $this->reports_to_name = '';
774                 }
775
776                 $this->_create_proper_name_field();
777         }
778
779         public function retrieve_user_id(
780             $user_name
781             )
782         {
783             $userFocus = new User;
784             $userFocus->retrieve_by_string_fields(array('user_name'=>$user_name));
785             if ( empty($userFocus->id) )
786                 return false;
787
788         return $userFocus->id;
789         }
790
791         /**
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): ______________________________________..
796          */
797         function verify_data($ieVerified=true) {
798                 global $mod_strings, $current_user;
799                 $verified = TRUE;
800
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;
811                                         break;
812                                 }
813                                 if ($check_user == $this->id) {
814                                         $reports_to_self = 1;
815                                         break;
816                                 }
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'];
823                         }
824
825                         if ($reports_to_self == 1) {
826                                 $this->error_string .= $mod_strings['ERR_REPORT_LOOP'];
827                                 $verified = FALSE;
828                         }
829                 }
830
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);
835
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'];
838                         $verified = FALSE;
839                 }
840
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";
844             }else{
845                 $query = "SELECT user_name from users where is_admin = 'on' AND deleted=0";
846             }
847                         $result = $this->db->query($query, true, "Error selecting possible duplicate users: ");
848                         $remaining_admins = $this->db->getRowCount($result);
849
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'];
853                                 $verified = FALSE;
854                         }
855                 }
856                 ///////////////////////////////////////////////////////////////////////
857                 ////    InboundEmail verification failure
858                 if(!$ieVerified) {
859                         $verified = false;
860                         $this->error_string .= '<br />'.$mod_strings['ERR_EMAIL_NO_OPTS'];
861                 }
862
863                 return $verified;
864         }
865
866         function get_list_view_data() {
867
868                 global $current_user;
869
870                 $user_fields = $this->get_list_view_array();
871                 if ($this->is_admin)
872                         $user_fields['IS_ADMIN_IMAGE'] = SugarThemeRegistry::current()->getImage('check_inline', '');
873                 elseif (!$this->is_admin) $user_fields['IS_ADMIN'] = '';
874                 if ($this->is_group)
875                         $user_fields['IS_GROUP_IMAGE'] = SugarThemeRegistry::current()->getImage('check_inline', '');
876                 else
877                         $user_fields['IS_GROUP_IMAGE'] = '';
878                 $user_fields['NAME'] = empty ($this->name) ? '' : $this->name;
879
880                 $user_fields['REPORTS_TO_NAME'] = $this->reports_to_name;
881
882                 $user_fields['EMAIL1'] = $this->emailAddress->getPrimaryAddress($this);
883
884                 return $user_fields;
885         }
886
887         function list_view_parse_additional_sections(& $list_form, $xTemplateSection) {
888                 return $list_form;
889         }
890
891         function save_relationship_changes($is_update) {
892         }
893
894
895
896
897         function create_export_query($order_by, $where) {
898                 include('modules/Users/field_arrays.php');
899
900                 $cols = '';
901                 foreach($fields_array['User']['export_fields'] as $field) {
902                         $cols .= (empty($cols)) ? '' : ', ';
903                         $cols .= $field;
904                 }
905
906                 $query = "SELECT {$cols} FROM users ";
907
908                 $where_auto = " users.deleted = 0";
909
910                 if ($where != "")
911                         $query .= " WHERE $where AND ".$where_auto;
912                 else
913                         $query .= " WHERE ".$where_auto;
914
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";
919                 }
920
921                 if ($order_by != "")
922                         $query .= " ORDER BY $order_by";
923                 else
924                         $query .= " ORDER BY users.user_name";
925
926                 return $query;
927         }
928
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): ______________________________________..
933         */
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());
938         }
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());
943         }
944
945         /**
946          * generates Javascript to display I-E mail counts, both personal and group
947          */
948         function displayEmailCounts() {
949                 global $theme;
950                 $new = translate('LBL_NEW', 'Emails');
951                 $default = 'index.php?module=Emails&action=ListView&assigned_user_id='.$this->id;
952                 $count = '';
953                 $verts = array('Love', 'Links', 'Pipeline', 'RipCurl', 'SugarLite');
954
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)) {
959                                 $count .= '<br />';
960                         } else {
961                                 $count .= '&nbsp;&nbsp;&nbsp;&nbsp;';
962                         }
963                         $count .= '<a href='.$default.'&type=inbound>'.translate('LBL_LIST_TITLE_MY_INBOX', 'Emails').': ('.$a['c'].' '.$new.')</a>';
964
965                         if(!in_array($theme, $verts)) {
966                                 $count .= ' - ';
967                         }
968                 }
969
970                 $r = $this->db->query('SELECT id FROM users WHERE users.is_group = 1 AND deleted = 0');
971                 $groupIds = '';
972                 $groupNew = '';
973                 while($a = $this->db->fetchByAssoc($r)) {
974                         if($groupIds != '') {$groupIds .= ', ';}
975                         $groupIds .= "'".$a['id']."'";
976                 }
977
978                 $total = 0;
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);
985                                 if($a['c'] > 0) {
986                                         $total = $a['c'];
987                                 }
988                         }
989                 }
990                 if(in_array($theme, $verts)) $count .= '<br />';
991                 if(empty($count)) $count .= '&nbsp;&nbsp;&nbsp;&nbsp;';
992                 $count .= '<a href=index.php?module=Emails&action=ListViewGroup>'.translate('LBL_LIST_TITLE_GROUP_INBOX', 'Emails').': ('.$total.' '.$new.')</a>';
993
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.'";';
998                 $out .= '</script>';
999
1000                 echo $out;
1001         }
1002
1003         function getPreferredEmail() {
1004                 $ret = array ();
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'];
1012                 } // if
1013                 $fullName = from_html($fullName);
1014                 $ret['name'] = $fullName;
1015                 $ret['email'] = $prefAddr;
1016                 return $ret;
1017         }
1018
1019         function getUsersNameAndEmail() {
1020                 $salutation = '';
1021                 $fullName = '';
1022                 if(!empty($this->salutation)) $salutation = $this->salutation;
1023
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;
1028                 }
1029                 $prefAddr = $this->emailAddress->getPrimaryAddress($this);
1030
1031                 if (empty ($prefAddr)) {
1032                         $prefAddr = $this->emailAddress->getReplyToAddress($this);
1033                 }
1034                 return array('email' => $prefAddr , 'name' => $fullName);
1035
1036         } // fn
1037
1038         function getSystemDefaultNameAndEmail() {
1039
1040                 $email = new Email();
1041                 $return = $email->getSystemDefaultEmail();
1042                 $prefAddr = $return['email'];
1043                 $fullName = $return['name'];
1044                 return array('email' => $prefAddr , 'name' => $fullName);
1045         } // fn
1046
1047         /**
1048          * sets User email default in config.php if not already set by install - i.
1049          * e., upgrades
1050          */
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;
1058         }
1059
1060     /**
1061      * returns User's email address based on descending order of preferences
1062      *
1063      * @param string id GUID of target user if needed
1064      * @return array Assoc array for an email and name
1065      */
1066     function getEmailInfo($id='') {
1067         $user = $this;
1068         if(!empty($id)) {
1069             $user = new User();
1070             $user->retrieve($id);
1071         }
1072
1073         // from name
1074         $fromName = $user->getPreference('mail_fromname');
1075         if(empty($fromName)) {
1076                 // cn: bug 8586 - localized name format
1077             $fromName = $user->full_name;
1078         }
1079
1080         // from address
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;
1087             } else {
1088                 $r = $user->db->query("SELECT value FROM config WHERE name = 'fromaddress'");
1089                 $a = $user->db->fetchByAssoc($r);
1090                 $fromddr = $a['value'];
1091             }
1092         }
1093
1094         $ret['name'] = $fromName;
1095         $ret['email'] = $fromaddr;
1096
1097         return $ret;
1098     }
1099
1100         /**
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
1106          * @param contact_id
1107          * @param return_module
1108          * @param return_action
1109          * @param return_id
1110          * @param class
1111          */
1112         function getEmailLink2($emailAddress, &$focus, $contact_id='', $ret_module='', $ret_action='DetailView', $ret_id='', $class='') {
1113                 $emailLink = '';
1114                 global $sugar_config;
1115
1116                 if(!isset($sugar_config['email_default_client'])) {
1117                         $this->setDefaultsInConfig();
1118                 }
1119
1120                 $userPref = $this->getPreference('email_link_type');
1121                 $defaultPref = $sugar_config['email_default_client'];
1122                 if($userPref != '') {
1123                         $client = $userPref;
1124                 } else {
1125                         $client = $defaultPref;
1126                 }
1127
1128                 if($client == 'sugar') {
1129                         $salutation = '';
1130                         $fullName = '';
1131                         $email = '';
1132                         $to_addrs_ids = '';
1133                         $to_addrs_names = '';
1134                         $to_addrs_emails = '';
1135
1136                         if(!empty($focus->salutation)) $salutation = $focus->salutation;
1137
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;
1142                         }
1143
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;
1151                         }
1152
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 . '&nbsp;&lt;' . $emailAddress . '&gt;').
1161                                 '&return_module='.$ret_module.
1162                                 '&return_action='.$ret_action.
1163                                 '&return_id='.$ret_id;
1164
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);
1171
1172                 $emailLink = "<a href='javascript:void(0);' onclick='SUGAR.quickCompose.init($j_quickComposeOptions);' class='$class'>";
1173
1174                 } else {
1175                         // straight mailto:
1176                         $emailLink = '<a href="mailto:'.$emailAddress.'" class="'.$class.'">';
1177                 }
1178
1179                 return $emailLink;
1180         }
1181
1182         /**
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
1188          * @param contact_id
1189          * @param return_module
1190          * @param return_action
1191          * @param return_id
1192          * @param class
1193          */
1194         function getEmailLink($attribute, &$focus, $contact_id='', $ret_module='', $ret_action='DetailView', $ret_id='', $class='') {
1195             $emailLink = '';
1196                 global $sugar_config;
1197
1198                 if(!isset($sugar_config['email_default_client'])) {
1199                         $this->setDefaultsInConfig();
1200                 }
1201
1202                 $userPref = $this->getPreference('email_link_type');
1203                 $defaultPref = $sugar_config['email_default_client'];
1204                 if($userPref != '') {
1205                         $client = $userPref;
1206                 } else {
1207                         $client = $defaultPref;
1208                 }
1209
1210                 if($client == 'sugar') {
1211                         $salutation = '';
1212                         $fullName = '';
1213                         $email = '';
1214                         $to_addrs_ids = '';
1215                         $to_addrs_names = '';
1216                         $to_addrs_emails = '';
1217
1218                         if(!empty($focus->salutation)) $salutation = $focus->salutation;
1219
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;
1224                         }
1225                         if(!empty($focus->$attribute)) {
1226                                 $email = $focus->$attribute;
1227                         }
1228
1229
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;
1237                         }
1238
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 . '&nbsp;&lt;' . $email . '&gt;').
1247                                 '&return_module='.$ret_module.
1248                                 '&return_action='.$ret_action.
1249                                 '&return_id='.$ret_id;
1250
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'>";
1256
1257                 } else {
1258                         // straight mailto:
1259                         $emailLink = '<a href="mailto:'.$focus->$attribute.'" class="'.$class.'">';
1260                 }
1261
1262                 return $emailLink;
1263         }
1264
1265
1266         /**
1267          * gets a human-readable explanation of the format macro
1268          * @return string Human readable name format
1269          */
1270         function getLocaleFormatDesc() {
1271                 global $locale;
1272                 global $mod_strings;
1273                 global $app_strings;
1274
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'];
1279
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'];
1284
1285                 $macro = $locale->getLocaleFormatMacro();
1286
1287                 $ret1 = '';
1288                 $ret2 = '';
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>";
1293                         } else {
1294                                 $ret1 .= $macro{$i};
1295                                 $ret2 .= $macro{$i};
1296                         }
1297                 }
1298                 return $ret1."<br />".$ret2;
1299         }
1300
1301
1302         /**
1303          * Whether or not based on the user's locale if we should show the last name first.
1304          *
1305          * @return bool
1306          */
1307         public function showLastNameFirst(){
1308                 global $locale;
1309         $localeFormat = $locale->getLocaleFormatMacro($this);
1310                 if ( strpos($localeFormat,'l') > strpos($localeFormat,'f') ) {
1311                     return false;
1312         }else {
1313                 return true;
1314         }
1315         }
1316
1317
1318
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);
1322
1323                 //if this is being called from webservices, then run additional code
1324                 if(!empty($GLOBALS['soap_server_object'])){
1325
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)
1328                 if($singleSelect)
1329                 {
1330                         $ret_array['order_by'] = ' Group By '.$this->table_name.'.id '.$ret_array['order_by'];
1331                 }
1332                 }
1333
1334                 //return array or query string
1335                 if($return_array)
1336         {
1337                 return $ret_array;
1338         }
1339
1340         return  $ret_array['select'] . $ret_array['from'] . $ret_array['where']. $ret_array['order_by'];
1341
1342
1343
1344    }
1345
1346 }