Projet

Général

Profil

Paste
Statistiques
| Branche: | Révision:

ryxeo-glpi-git / inc / user.class.php @ b67d8923

Historique | Voir | Annoter | Télécharger (36,3 ko)

1
<?php
2

    
3

    
4
/*
5
 * @version $Id: user.class.php 7875 2009-01-23 15:16:47Z moyo $
6
 -------------------------------------------------------------------------
7
 GLPI - Gestionnaire Libre de Parc Informatique
8
 Copyright (C) 2003-2009 by the INDEPNET Development Team.
9

10
 http://indepnet.net/   http://glpi-project.org
11
 -------------------------------------------------------------------------
12

13
 LICENSE
14

15
 This file is part of GLPI.
16

17
 GLPI is free software; you can redistribute it and/or modify
18
 it under the terms of the GNU General Public License as published by
19
 the Free Software Foundation; either version 2 of the License, or
20
 (at your option) any later version.
21

22
 GLPI is distributed in the hope that it will be useful,
23
 but WITHOUT ANY WARRANTY; without even the implied warranty of
24
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25
 GNU General Public License for more details.
26

27
 You should have received a copy of the GNU General Public License
28
 along with GLPI; if not, write to the Free Software
29
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
30
 --------------------------------------------------------------------------
31
 */
32

    
33
// ----------------------------------------------------------------------
34
// Original Author of file:
35
// Purpose of file:
36
// ----------------------------------------------------------------------
37
// And Marco Gaiarin for ldap features 
38

    
39
if (!defined('GLPI_ROOT')) {
40
        die("Sorry. You can't access directly to this file");
41
}
42

    
43
class User extends CommonDBTM {
44

    
45
        /**
46
         * Constructor
47
        **/
48
        function User() {
49
                global $CFG_GLPI;
50

    
51
                $this->table = "glpi_users";
52
                $this->type = USER_TYPE;
53

    
54
                $this->date_mod_blacklist = array('last_login');
55

    
56
                $this->fields['tracking_order'] = 0;
57
                if (isset ($CFG_GLPI["default_language"])){
58
                        $this->fields['language'] = $CFG_GLPI["default_language"];
59
                } else {
60
                        $this->fields['language'] = "en_GB";
61
                }
62

    
63
        }
64
        function defineOnglets($withtemplate) {
65
                global $LANG;
66

    
67

    
68
                $ong[1] = $LANG["title"][26]; // principal
69

    
70
                $ong[4]=$LANG["Menu"][36];
71

    
72
                $ong[2] = $LANG["common"][1]; // materiel
73
                if (haveRight("show_all_ticket", "1")){
74
                        $ong[3] = $LANG["title"][28]; // tickets
75
                }
76
                if (haveRight("reservation_central", "r")){
77
                        $ong[11] = $LANG["Menu"][17];
78
                }
79
                if (haveRight("user_auth_method", "w")){
80
                        $ong[12] = $LANG["ldap"][12];
81
                }
82

    
83
                return $ong;
84
        }
85

    
86
        function post_getEmpty () {
87
                $this->fields["active"]=1;
88
        }
89

    
90
        function pre_deleteItem($ID){
91
                global $LANG,$DB;
92

    
93
                $entities=getUserEntities($ID);
94
                $view_all=isViewAllEntities();
95
                // Have right on all entities ?
96
                $all=true;
97
                if (!$view_all){
98
                        foreach ($entities as $ent){
99
                                if (!haveAccessToEntity($ent)){
100
                                        $all=false;
101
                                }
102
                        }
103
                }
104
                if ($all){ // Mark as deleted
105
                        return true;
106
                } else { // only delete profile
107
                        foreach ($entities as $ent){
108
                                if (haveAccessToEntity($ent)){
109
                                        $all=false;
110
                                        $query = "DELETE FROM glpi_users_profiles WHERE (FK_users = '$ID' AND FK_entities='$ent')";
111
                                        $DB->query($query);
112
                                }
113
                        }
114
                        return false;
115
                }
116
        }
117
        function cleanDBonMarkDeleted($ID) {
118
        }
119

    
120
        function cleanDBonPurge($ID) {
121
                global $DB;
122

    
123
                $query = "DELETE FROM glpi_users_profiles WHERE (FK_users = '$ID')";
124
                $DB->query($query);
125

    
126
                $query = "DELETE FROM glpi_users_groups WHERE FK_users = '$ID'";
127
                $DB->query($query);
128

    
129
                $query = "DELETE FROM glpi_display WHERE FK_users = '$ID'";
130
                $DB->query($query);
131
                // Delete private reminder
132
                $query = "DELETE FROM glpi_reminder WHERE FK_users = '$ID' AND private=1";
133
                $DB->query($query);
134
                // Set no user to public reminder
135
                $query = "UPDATE glpi_reminder SET FK_users = 0 WHERE FK_users = '$ID'";
136
                $DB->query($query);
137
                // Delete private bookmark
138
                $query = "DELETE FROM glpi_bookmark WHERE FK_users = '$ID' AND private=1";
139
                $DB->query($query);
140
                // Set no user to public bookmark
141
                $query = "UPDATE glpi_bookmark SET FK_users = 0 WHERE FK_users = '$ID'";
142
                $DB->query($query);
143
                
144
        }
145

    
146
        /**
147
         * Retrieve an item from the database using its login
148
         *
149
         *@param $name login of the user
150
         *@return true if succeed else false
151
         * 
152
        **/        
153
        function getFromDBbyName($name) {
154
                global $DB;
155
                $query = "SELECT * FROM glpi_users WHERE (name = '" . $name . "')";
156
                if ($result = $DB->query($query)) {
157
                        if ($DB->numrows($result) != 1) {
158
                                return false;
159
                        }
160
                        $this->fields = $DB->fetch_assoc($result);
161
                        if (is_array($this->fields) && count($this->fields)) {
162
                                return true;
163
                        } else {
164
                                return false;
165
                        }
166
                }
167
                return false;
168
        }
169

    
170
        function prepareInputForAdd($input) {
171
                global $CFG_GLPI,$DB,$LANG;
172

    
173
        
174
                // Check if user does not exists
175
                $query="SELECT * FROM glpi_users WHERE name='".$input['name']."';";
176
                $result=$DB->query($query);
177
                if ($DB->numrows($result)>0){
178
                        addMessageAfterRedirect($LANG["setup"][606]);
179
                        return false;
180
                }
181

    
182
                // Preferences
183
                if (!isset($input["language"])){
184
                        $input["language"]=$CFG_GLPI["default_language"];
185
                }
186

    
187
                if (!isset($input["list_limit"])){
188
                        $input["list_limit"]=$CFG_GLPI["list_limit"];
189
                }        
190
                
191
                // Add User, nasty hack until we get PHP4-array-functions
192
                if (isset ($input["password"])) {
193
                        if (empty ($input["password"])) {
194
                                unset ($input["password"]);
195
                        } else {
196
                                $input["password_md5"] = md5(unclean_cross_side_scripting_deep(stripslashes($input["password"])));
197
                                $input["password"] = "";
198
                        }
199
                }
200
                if (isset ($input["_extauth"])) {
201
                        $input["password"] = "";
202
                        $input["password_md5"] = "";
203
                }
204
                // change email_form to email (not to have a problem with preselected email)
205
                if (isset ($input["email_form"])) {
206
                        $input["email"] = $input["email_form"];
207
                        unset ($input["email_form"]);
208
                }
209

    
210
                // Force DB default values : not really needed
211
                if (!isset($input["active"])){
212
                        $input["active"]=1;
213
                }
214

    
215
                if (!isset($input["deleted"])){
216
                        $input["deleted"]=0;
217
                }
218

    
219
                if (!isset($input["FK_entities"])){
220
                        $input["FK_entities"]=0;
221
                }
222

    
223
                if (!isset($input["FK_profiles"])){
224
                        $input["FK_profiles"]=0;
225
                }
226

    
227
                if (!isset($input["auth_method"])){
228
                        $input["auth_method"]=-1;
229
                }
230

    
231

    
232
                return $input;
233
        }
234

    
235
        function post_addItem($newID, $input) {
236
                global $DB;
237

    
238
                $input["ID"]=$newID;
239

    
240
                $this->syncLdapGroups($input);
241
                $this->applyRightRules($input);
242

    
243
                // Add default profile
244
                if ($input['auth_method']==AUTH_DB_GLPI || (isAlternateAuthWithLdap($input['auth_method']))){
245
                        $sql_default_profile = "SELECT ID FROM glpi_profiles WHERE is_default=1";
246
                        $result = $DB->query($sql_default_profile);
247
                        if ($DB->numrows($result)){
248
                                $right=$DB->result($result,0,0);
249
                                if (isset($input["FK_entities"])){
250
                                        $affectation["FK_entities"] = $input["FK_entities"];
251
                                } else if (isset($_SESSION['glpiactive_entity'])){
252
                                        $affectation["FK_entities"] = $_SESSION['glpiactive_entity'];
253
                                } else {
254
                                        $affectation["FK_entities"] = 0;
255
                                }
256
                                $affectation["FK_profiles"] = $DB->result($result,0,0);
257
                                $affectation["FK_users"] = $input["ID"];
258
                                $affectation["recursive"] = 0;
259
                                $affectation["dynamic"] = 0;
260
                                addUserProfileEntity($affectation);
261
                        }
262
                }
263
        }
264

    
265
        function prepareInputForUpdate($input) {
266
                global  $LANG;
267

    
268

    
269
                if (isset ($input["password"])&&empty($input["password"])) {
270
                        unset($input["password"]);
271
                }
272

    
273

    
274
                if (isset ($input["password"])) {
275
                        $input["password_md5"] = md5(unclean_cross_side_scripting_deep(stripslashes($input["password"])));
276
                        $input["password"] = "";
277
                }
278

    
279
                // change email_form to email (not to have a problem with preselected email)
280
                if (isset ($input["email_form"])) {
281
                        $input["email"] = $input["email_form"];
282
                        unset ($input["email_form"]);
283
                }
284

    
285
                // Update User in the database
286
                if (!isset ($input["ID"]) && isset ($input["name"])) {
287
                        if ($this->getFromDBbyName($input["name"]))
288
                                $input["ID"] = $this->fields["ID"];
289
                }
290

    
291
                if (isset ($_SESSION["glpiID"]) && isset ($input["language"]) && $_SESSION["glpiID"] == $input['ID']) {
292
                        $_SESSION["glpilanguage"] = $input["language"];
293
                }
294
                if (isset ($_SESSION["glpiID"]) && isset ($input["FK_entities"]) && $_SESSION["glpiID"] == $input['ID']) {
295
                        $_SESSION["glpidefault_entity"] = $input["FK_entities"];
296
                }
297
                if (isset ($_SESSION["glpiID"]) && isset ($input["tracking_order"]) && $_SESSION["glpiID"] == $input['ID']) {
298
                        $_SESSION["glpitracking_order"] = $input["tracking_order"];
299
                }
300
                if (isset ($_SESSION["glpiID"]) && isset ($input["list_limit"]) && $_SESSION["glpiID"] == $input['ID']) {
301
                        $_SESSION["glpilist_limit"] = $input["list_limit"];
302
                }
303

    
304
                $this->syncLdapGroups($input);
305

    
306
                $this->applyRightRules($input);
307

    
308
                return $input;
309
        }
310

    
311

    
312
        function post_updateItem($input, $updates, $history=1) {
313
                // Clean header cache for the user
314
                if (in_array("language", $updates) && isset ($input["ID"])) {
315
                        cleanCache("GLPI_HEADER_".$input["ID"]);
316
                }
317
        }
318

    
319
        // SPECIFIC FUNCTIONS
320
        /**
321
         * Apply rules to determine dynamic rights of the user
322
         *
323
         *@param $input data used to apply rules
324
        **/        
325
        function applyRightRules($input){
326
                global $DB;
327
                if (isset($input["auth_method"])&&($input["auth_method"] == AUTH_LDAP || $input["auth_method"]== AUTH_MAIL|| isAlternateAuthWithLdap($input["auth_method"])))
328
                if (isset ($input["ID"]) &&$input["ID"]>0&& isset ($input["_ldap_rules"]) && count($input["_ldap_rules"])) {
329

    
330
                        //TODO : do not erase all the dynamic rights, but compare it with the ones in DB
331
                        //and add/update/delete only if it's necessary !
332
                        if (isset($input["_ldap_rules"]["rules_entities_rights"]))
333
                                $entities_rules = $input["_ldap_rules"]["rules_entities_rights"];
334
                        else
335
                                $entities_rules = array();
336
        
337
                        if (isset($input["_ldap_rules"]["rules_entities"]))
338
                                $entities = $input["_ldap_rules"]["rules_entities"];
339
                        else 
340
                                $entities = array();
341
                                
342
                        if (isset($input["_ldap_rules"]["rules_rights"]))
343
                                $rights = $input["_ldap_rules"]["rules_rights"];
344
                        else
345
                                $rights = array();
346

    
347
                        //purge dynamic rights
348
                        $this->purgeDynamicProfiles();
349
                        
350
                        //For each affectation -> write it in DB                
351
                        foreach($entities_rules as $entity)
352
                        {
353
                                $affectation["FK_entities"] = $entity[0];
354
                                $affectation["FK_profiles"] = $entity[1];
355
                                $affectation["recursive"] = $entity[2];
356
                                $affectation["FK_users"] = $input["ID"];
357
                                $affectation["dynamic"] = 1;
358
                                addUserProfileEntity($affectation);
359
                        }
360
        
361
                        if (count($entities)>0&&count($rights)==0){
362
                                //If no dynamics profile is provided : get the profil by default if not existing profile
363
                                $exist_profile = "SELECT ID FROM glpi_users_profiles WHERE FK_users='".$input["ID"]."'";
364
                                $result = $DB->query($exist_profile);
365
                                if ($DB->numrows($result)==0){
366
                                        $sql_default_profile = "SELECT ID FROM glpi_profiles WHERE is_default=1";
367
                                        $result = $DB->query($sql_default_profile);
368
                                        if ($DB->numrows($result))
369
                                        {
370
                                                $rights[]=$DB->result($result,0,0);
371
                                        }
372
                                }
373
                        }
374

    
375
                        if (count($rights)>0&&count($entities)>0){
376
                                foreach($entities as $entity){
377
                                        foreach($rights as $right){
378
                                                $affectation["FK_entities"] = $entity[0];
379
                                                $affectation["FK_profiles"] = $right;
380
                                                $affectation["FK_users"] = $input["ID"];
381
                                                $affectation["recursive"] = $entity[1];
382
                                                $affectation["dynamic"] = 1;
383
                                                addUserProfileEntity($affectation);
384
                                        }
385
                                }
386
                        }
387
                        
388
                        //Unset all the temporary tables
389
                        unset($input["_ldap_rules"]);
390
                }
391

    
392
        }
393
        /**
394
         * Synchronise LDAP group of the user
395
         *
396
         *@param $input data used to sync
397
        **/        
398
        function syncLdapGroups($input){
399
                global $DB;
400

    
401
                if (isset($input["auth_method"])&&($input["auth_method"]==AUTH_LDAP || isAlternateAuthWithLdap($input['auth_method']))){
402
                        if (isset ($input["ID"]) && $input["ID"]>0) {
403
                                $auth_method = getAuthMethodsByID($input["auth_method"], $input["id_auth"]);
404
                                
405
                                if (count($auth_method)){
406
                                        if (!isset($input["_groups"])){
407
                                                $input["_groups"]=array();
408
                                        }
409
                                        // Clean groups
410
                                        $input["_groups"] = array_unique ($input["_groups"]);
411
        
412
        
413
                                        $WHERE = "";
414
                                        switch ($auth_method["ldap_search_for_groups"]) {
415
                                                case 0 : // user search
416
                                                        $WHERE = "AND (glpi_groups.ldap_field <> '' AND glpi_groups.ldap_field IS NOT NULL AND glpi_groups.ldap_value<>'' AND glpi_groups.ldap_value IS NOT NULL )";
417
                                                        break;
418
                                                case 1 : // group search
419
                                                        $WHERE = "AND (ldap_group_dn<>'' AND ldap_group_dn IS NOT NULL )";
420
                                                        break;
421
                                                case 2 : // user+ group search
422
                                                        $WHERE = "AND ((glpi_groups.ldap_field <> '' AND glpi_groups.ldap_field IS NOT NULL AND glpi_groups.ldap_value<>'' AND glpi_groups.ldap_value IS NOT NULL) 
423
                                                                OR (ldap_group_dn<>'' AND ldap_group_dn IS NOT NULL) )";
424
                                                        break;
425
        
426
                                        }
427
                                        // Delete not available groups like to LDAP
428
                                        $query = "SELECT glpi_users_groups.ID, glpi_users_groups.FK_groups 
429
                                                                FROM glpi_users_groups 
430
                                                                LEFT JOIN glpi_groups ON (glpi_groups.ID = glpi_users_groups.FK_groups) 
431
                                                                WHERE glpi_users_groups.FK_users='" . $input["ID"] . "' $WHERE";
432
        
433
                                        $result = $DB->query($query);
434
                                        if ($DB->numrows($result) > 0) {
435
                                                while ($data = $DB->fetch_array($result)){
436
                                                        if (!in_array($data["FK_groups"], $input["_groups"])) {
437
                                                                deleteUserGroup($data["ID"]);
438
                                                        } else {
439
                                                                // Delete found item in order not to add it again
440
                                                                unset($input["_groups"][array_search($data["FK_groups"], $input["_groups"])]);
441
                                                        }
442
                                                }
443
                                        }
444
                                        
445
                                        //If the user needs to be added to one group or more
446
                                        if (count($input["_groups"])>0)
447
                                        {
448
                                                foreach ($input["_groups"] as $group) {
449
                                                        addUserGroup($input["ID"], $group);
450
                                                }
451
                                                unset ($input["_groups"]);
452
                                        }
453
                                }
454
                        }
455
                }
456
        }
457

    
458
        /**
459
         * Get the name of the current user
460
         * @return string containing name of the user
461
        **/        
462
        function getName() {
463
                return formatUserName($this->fields["ID"],$this->fields["name"],$this->fields["realname"],$this->fields["firstname"]);
464
        }
465

    
466
        /**
467
         * Function that try to load from LDAP the user information...
468
         *
469
         * @param $ldap_connection ldap connection descriptor
470
         * @param $ldap_method LDAP method
471
         * @param $userdn Basedn of the user
472
         * @param $login User Login
473
         * @param $password User Password
474
         *
475
         * @return String : basedn of the user / false if not founded
476
         */
477
        function getFromLDAP($ldap_connection,$ldap_method, $userdn, $login, $password = "") {
478
                global $DB;
479

    
480
                // we prevent some delay...
481
                if (empty ($ldap_method["ldap_host"])) {
482
                        return false;
483
                }
484

    
485
                if ($ldap_connection) {
486
                        //Set all the search fields
487
                        $this->fields['password'] = "";
488
                        $this->fields['password_md5'] = "";
489
                        
490
                        $fields=getLDAPSyncFields($ldap_method);
491
        
492
                        $fields = array_filter($fields);
493
                        $f = array_values($fields);
494
                                                        
495
                        $sr = @ ldap_read($ldap_connection, $userdn, "objectClass=*", $f);
496
                        $v = ldap_get_entries($ldap_connection, $sr);
497
                        
498
                        if (!is_array($v) || count($v) == 0 || empty ($v[0][$fields['name']][0]))
499
                                return false;
500

    
501
                        foreach ($fields as $k => $e) {
502
                                        if (!empty($v[0][$e][0]))
503
                                        $this->fields[$k] = addslashes($v[0][$e][0]);
504
                                        else
505
                                        $this->fields[$k] = "";
506
                        }
507

    
508
                        // Get group fields
509
                        $query_user = "SELECT ID,ldap_field, ldap_value FROM glpi_groups WHERE ldap_field<>'' AND ldap_field IS NOT NULL AND ldap_value<>'' AND ldap_value IS NOT NULL";
510
                        $query_group = "SELECT ID,ldap_group_dn FROM glpi_groups WHERE ldap_group_dn<>'' AND ldap_group_dn IS NOT NULL";
511

    
512
                        $group_fields = array ();
513
                        $groups = array ();
514
                        $v = array ();
515
                        //The groupes are retrived by looking into an ldap user object
516
                        if ($ldap_method["ldap_search_for_groups"] == 0 || $ldap_method["ldap_search_for_groups"] == 2) {
517

    
518
                                $result = $DB->query($query_user);
519

    
520
                                if ($DB->numrows($result) > 0) {
521
                                        while ($data = $DB->fetch_assoc($result)) {
522
                                                $group_fields[] = strtolower($data["ldap_field"]);
523
                                                $groups[strtolower($data["ldap_field"])][$data["ID"]] = $data["ldap_value"];
524
                                        }
525
                                        $group_fields = array_unique($group_fields);
526
                                        
527
                                        //Need to sort the array because edirectory don't like it!
528
                                        sort($group_fields);
529

    
530
                                        // If the groups must be retrieve from the ldap user object
531
                                        $sr = @ ldap_read($ldap_connection, $userdn, "objectClass=*", $group_fields);
532
                                        $v = ldap_get_entries($ldap_connection, $sr);
533
                                }
534
                        }
535
                        //The groupes are retrived by looking into an ldap group object
536
                        if ($ldap_method["ldap_search_for_groups"] == 1 || $ldap_method["ldap_search_for_groups"] == 2) {
537

    
538
                                $result = $DB->query($query_group);
539

    
540
                                if ($DB->numrows($result) > 0) {
541
                                        while ($data = $DB->fetch_assoc($result)) {
542
                                                $groups[$ldap_method["ldap_field_group_member"]][$data["ID"]] = $data["ldap_group_dn"];
543
                                        }
544
                                        if ($ldap_method["use_dn"])
545
                                                $user_tmp = $userdn;
546
                                        else
547
                                                //$user_tmp = $ldap_method["ldap_login"]."=".$login;
548
                                                //Don't add $ldap_method["ldap_login"]."=", because sometimes it may not work (for example with posixGroup)
549
                                                $user_tmp = $login;
550
                                                
551
                                        $v2 = $this->ldap_get_user_groups($ldap_connection, $ldap_method["ldap_basedn"], $user_tmp, $ldap_method["ldap_group_condition"], $ldap_method["ldap_field_group_member"],$ldap_method["use_dn"],$ldap_method["ldap_login"]);
552
                                        
553
                                        $v = array_merge($v, $v2);
554
                                }
555

    
556
                        }
557

    
558
                        if ($ldap_method["ldap_field_group"] == "dn")
559
                        {
560
                                for ($i=0;$i<count($v['count']);$i++) 
561
                                {
562
                                        //Try to find is DN in present: if yes, then extract only the OU from it
563
                                if (array_key_exists($ldap_method["ldap_field_group"],$v[$i]))
564
                                {
565
                                         list($null,$ou) = explode(",",$v[$i][$ldap_method["ldap_field_group"]],2);
566
                                $v[$i]['ou'] = array( $ou );
567
                                $v[$i]['count'] = 1;
568
                                }
569
                                }
570
                        }
571
                        
572
                        if (is_array($v) && count($v) > 0) {
573
                                foreach ($v as $attribute => $valattribute) {
574
                                        if (is_array($valattribute))
575
                                                foreach ($valattribute as $key => $val) {
576
                                                        if (is_array($val))
577
                                                                for ($i = 0; $i < count($val); $i++) {
578
                                                                         if (isset ($val[$i]))
579
                                                                                if ($group_found = array_search($val[$i], $groups[$key])) {
580
                                                                                        $this->fields["_groups"][] = $group_found;
581
                                                                                }
582
                                                                }
583
                                                }
584
                                }
585
                        }
586

    
587
                        //Only process rules if working on the master database
588
                        if (!$DB->isSlave())
589
                        {
590
                                //Instanciate the affectation's rule
591
                                $rule = new RightRuleCollection();
592
                                        
593
                                //Process affectation rules :
594
                                //we don't care about the function's return because all the datas are stored in session temporary
595
                                if (isset($this->fields["_groups"]))
596
                                        $groups = $this->fields["_groups"];
597
                                else
598
                                        $groups = array();        
599
                
600
                                $this->fields=$rule->processAllRules($groups,$this->fields,array("type"=>"LDAP","ldap_server"=>$ldap_method["ID"],"connection"=>$ldap_connection,"userdn"=>$userdn));
601
                                
602
                                //Hook to retrieve more informations for ldap
603
                                $this->fields = doHookFunction("retrieve_more_data_from_ldap", $this->fields);
604
                        }
605
                        return true;
606
                }
607
                return false;
608

    
609
        } // getFromLDAP()
610

    
611
        /**
612
         * Get all the group a user belongs to
613
         *
614
         * @param $ds ldap connection 
615
         * @param $ldap_base_dn Basedn used
616
         * @param $user_dn Basedn of the user
617
         * @param $group_condition group search condition
618
         * @param $group_field_member group field member in a user object
619
         *
620
         * @return String : basedn of the user / false if not founded
621
         */
622
        function ldap_get_user_groups($ds, $ldap_base_dn, $user_dn, $group_condition, $group_field_member,$use_dn,$login_field) {
623

    
624
                $groups = array ();
625
                $listgroups = array ();
626

    
627
                //Only retrive cn and member attributes from groups
628
                $attrs = array (
629
                        "dn"
630
                );
631

    
632
                if (!$use_dn)                
633
                        $filter = "(& $group_condition (|($group_field_member=$user_dn)($group_field_member=$login_field=$user_dn)))";
634
                else
635
                        $filter = "(& $group_condition ($group_field_member=$user_dn))";
636
                        
637
                //Perform the search
638
                $sr = ldap_search($ds, $ldap_base_dn, $filter, $attrs);
639

    
640
                //Get the result of the search as an array
641
                $info = ldap_get_entries($ds, $sr);
642

    
643
                //Browse all the groups
644
                for ($i = 0; $i < count($info); $i++) {
645
                        //Get the cn of the group and add it to the list of groups
646
                        if (isset ($info[$i]["dn"]) && $info[$i]["dn"] != '')
647
                                $listgroups[$i] = $info[$i]["dn"];
648
                }
649

    
650
                //Create an array with the list of groups of the user
651
                $groups[0][$group_field_member] = $listgroups;
652
                //Return the groups of the user
653
                return $groups;
654
        }
655

    
656
        /**
657
         * Function that try to load from IMAP the user information...
658
         *
659
         * @param $mail_method mail method description array
660
         * @param $name login of the user
661
         */
662
        function getFromIMAP($mail_method, $name) {
663
                global $DB;
664
                
665
                // we prevent some delay..
666
                if (empty ($mail_method["imap_host"])) {
667
                        return false;
668
                }
669

    
670
                // some defaults...
671
                $this->fields['password'] = "";
672
                $this->fields['password_md5'] = "";
673
                if (ereg("@", $name)){
674
                        $this->fields['email'] = $name;
675
                } else {
676
                        $this->fields['email'] = $name . "@" . $mail_method["imap_host"];
677
                }
678

    
679
                $this->fields['name'] = $name;
680

    
681
                if (!$DB->isSlave())
682
                {
683
                        //Instanciate the affectation's rule
684
                        $rule = new RightRuleCollection();
685
                                
686
                        //Process affectation rules :
687
                        //we don't care about the function's return because all the datas are stored in session temporary
688
                        if (isset($this->fields["_groups"]))
689
                                $groups = $this->fields["_groups"];
690
                        else
691
                                $groups = array();        
692
                        $this->fields=$rule->processAllRules($groups,$this->fields,array("type"=>"MAIL","mail_server"=>$mail_method["ID"],"email"=>$this->fields["email"]));
693
                }
694
                return true;
695

    
696
        } // getFromIMAP()              
697

    
698
        /**
699
         * Blank passwords field of a user in the DB 
700
         * todo for external auth users
701
         **/
702
        function blankPassword() {
703
                global $DB;
704
                if (!empty ($this->fields["name"])) {
705

    
706
                        $query = "UPDATE glpi_users SET password='' , password_md5='' WHERE name='" . $this->fields["name"] . "'";
707
                        $DB->query($query);
708
                }
709
        }
710

    
711
        /**
712
         * Print a good title for user pages
713
         *
714
         *@return nothing (display)
715
         **/
716
        function title() {
717
                global $LANG, $CFG_GLPI;
718

    
719
                $buttons = array ();
720
                $title = $LANG["Menu"][14];
721
                if (haveRight("user", "w")) {
722
                        $buttons["user.form.php?new=1"] = $LANG["setup"][2];
723
                        $title = "";
724
                        
725
                        if (haveRight("user_auth_method", "w")) {
726
                                if (useAuthLdap()) {
727
                                        $buttons["user.form.php?new=1&amp;ext_auth=1"] = $LANG["setup"][125];
728
                                        $buttons["ldap.php"] = $LANG["setup"][3];
729
                                        
730
                                } else if (useAuthExt()) {
731
                                        $buttons["user.form.php?new=1&amp;ext_auth=1"] = $LANG["setup"][125];
732
                                }
733
                        }
734
                }
735

    
736
                displayTitle($CFG_GLPI["root_doc"] . "/pics/users.png", $LANG["Menu"][14], $title, $buttons);
737
        }
738

    
739
        /**
740
         * Print the user form
741
         *
742
         *@param $target form target
743
         *@param $ID Integer : Id of the user
744
         *@param $withtemplate boolean : template or basic item
745
         *
746
         *@return boolean : user found
747
         **/
748
        function showForm($target, $ID, $withtemplate = '') {
749

    
750
                // Affiche un formulaire User
751
                global $CFG_GLPI, $LANG;
752

    
753
                if ($ID != $_SESSION["glpiID"] && !haveRight("user", "r"))
754
                        return false;
755

    
756
                $canedit = haveRight("user", "w");
757
                $canread = haveRight("user", "r");
758

    
759
                $spotted = false;
760
                $use_cache=true;
761
                if (empty ($ID)) {
762
                        $use_cache=false;
763
                        if ($this->getEmpty()){
764
                                $spotted = true;
765
                        }
766
                } else {
767
                        if ($this->getFromDB($ID)){
768
                                $entities=getUserEntities($ID);
769
                                $view_all=isViewAllEntities();
770
                                if (haveAccessToOneOfEntities($entities)||$view_all){
771
                                        $spotted = true;
772
                                }
773
                                $strict_entities=getUserEntities($ID,false);
774
                                if (!haveAccessToOneOfEntities($strict_entities)&&!$view_all){
775
                                        $canedit=false;
776
                                }
777
                        }
778
                }
779
                if ($spotted) {
780
                
781
                        $extauth = ! ($this->fields["auth_method"]==AUTH_DB_GLPI 
782
                                || ($this->fields["auth_method"]==NOT_YET_AUTHENTIFIED 
783
                                                && (!empty ($this->fields["password"]) || !empty ($this->fields["password_md5"])))
784
                                );
785
                
786
                        $this->showOnglets($ID, $withtemplate, $_SESSION['glpi_onglet']);
787
                        echo "<div class='center'>";
788
                        echo "<form method='post' name=\"user_manager\" action=\"$target\">";
789
                        if (empty ($ID)) {
790
                                echo "<input type='hidden' name='FK_entities' value='" . $_SESSION["glpiactive_entity"] . "'>";
791
                                echo "<input type='hidden' name='auth_method' value='1'>";
792
                        }
793
                        echo "<table class='tab_cadre_fixe'>";
794
                        echo "<tr><th colspan='4'>" . $LANG["common"][34] . " : " . $this->fields["name"] . "&nbsp;";
795
                        echo "<a href='" . $CFG_GLPI["root_doc"] . "/front/user.vcard.php?ID=$ID'>" . $LANG["common"][46] . "</a>";
796
                        echo "</th></tr>";
797
                        echo "<tr class='tab_bg_1'>";
798
                        echo "<td class='center'>" . $LANG["setup"][18] . "</td>";
799
                        // si on est dans le cas d'un ajout , cet input ne doit plus �re hiden
800
                        if ($this->fields["name"] == "") {
801
                                echo "<td><input  name='name' value=\"" . $this->fields["name"] . "\">";
802
                                echo "</td>";
803
                                // si on est dans le cas d'un modif on affiche la modif du login si ce n'est pas une auth externe
804
                        } else {
805
                                if (!empty ($this->fields["password_md5"])||$this->fields["auth_method"]==AUTH_DB_GLPI) {
806
                                        echo "<td>";
807
                                        autocompletionTextField("name", "glpi_users", "name", $this->fields["name"], 20);
808
                                } else {
809
                                        echo "<td class='center'><strong>" . $this->fields["name"] . "</strong>";
810
                                        echo "<input type='hidden' name='name' value=\"" . $this->fields["name"] . "\">";
811
                                }
812

    
813
                                echo "<input type='hidden' name='ID' value=\"" . $this->fields["ID"] . "\">";
814

    
815
                                echo "</td>";
816
                        }
817

    
818
                        //do some rights verification
819
                        if (haveRight("user", "w")) {
820
                                if ( !$extauth || empty($ID)) {
821
                                        echo "<td class='center'>" . $LANG["setup"][19] . ":</td><td><input type='password' name='password' value='' size='20'></td></tr>";
822
                                } else {
823
                                        echo "<td colspan='2'>&nbsp;</td></tr>";
824
                                }
825
                        } else
826
                                echo "<td colspan='2'>&nbsp;</td></tr>";
827

    
828
                        if (!$use_cache||!($CFG_GLPI["cache"]->start($ID . "_" . $_SESSION["glpilanguage"], "GLPI_" . $this->type))) {
829
                                echo "<tr class='tab_bg_1'><td class='center'>" . $LANG["common"][48] . ":</td><td>";
830
                                autocompletionTextField("realname", "glpi_users", "realname", $this->fields["realname"], 20);
831
                                echo "</td>";
832
                                echo "<td class='center'>" . $LANG["common"][43] . ":</td><td>";
833
                                autocompletionTextField("firstname", "glpi_users", "firstname", $this->fields["firstname"], 20);
834
                                echo "</td></tr>";
835

    
836
                                echo "<tr class='tab_bg_1'><td class='center'>" . $LANG["common"][42] . ":</td><td>";
837
                                autocompletionTextField("mobile", "glpi_users", "mobile", $this->fields["mobile"], 20);
838
                                echo "</td>";
839
                                echo "<td class='center'>" . $LANG["setup"][14] . ":</td><td>";
840
                                autocompletionTextField("email_form", "glpi_users", "email", $this->fields["email"], 30);
841
                                if (!empty($ID)&&!isValidEmail($this->fields["email"])){
842
                                        echo "<span class='red'>&nbsp;".$LANG["mailing"][110]."</span>";
843
                                }
844
                                echo "</td></tr>";
845

    
846
                                echo "<tr class='tab_bg_1'><td class='center'>" . $LANG["help"][35] . ":</td><td>";
847
                                autocompletionTextField("phone", "glpi_users", "phone", $this->fields["phone"], 20);
848
                                echo "</td>";
849
                                echo "<td class='center'>" . $LANG["help"][35] . " 2:</td><td>";
850
                                autocompletionTextField("phone2", "glpi_users", "phone2", $this->fields["phone2"], 20);
851
                                echo "</td></tr>";
852

    
853
                                echo "<tr class='tab_bg_1'><td class='center'>" . $LANG["common"][15] . ":</td><td>";
854
                                if (!empty($ID)){
855
                                        if (count($entities)>0){
856
                                                dropdownValue("glpi_dropdown_locations", "location", $this->fields["location"],1,$entities);
857
                                        } else {
858
                                                echo "&nbsp;";
859
                                        }
860
                                } else {
861
                                        if (!isMultiEntitiesMode()){
862
                                                // Display all locations : only one entity
863
                                                dropdownValue("glpi_dropdown_locations", "location", $this->fields["location"],1);
864
                                        } else {
865
                                                echo "&nbsp;";
866
                                        }
867
                                }
868
                                echo "</td>";
869
                                echo "<td class='center'>".$LANG["common"][60]."</td><td>";
870
                                dropdownYesNo('active',$this->fields['active']);
871
                                echo "</td></tr>";
872

    
873
                                echo "<tr class='tab_bg_1' align='center'><td>" . $LANG["common"][25] . ":</td><td colspan='3'><textarea  cols='70' rows='3' name='comments' >" . $this->fields["comments"] . "</textarea></td>";
874
                                echo "</tr>";
875

    
876
                                //Authentications informations : auth method used and server used
877
                                //don't display is creation of a new user'
878
                                if (!empty ($ID)) {
879
                                        if (haveRight("user_auth_method", "r")){
880
                                                echo "<tr class='tab_bg_1' align='center'><td>" . $LANG["login"][10] . ":</td><td class='center'>";
881

    
882
                                                echo getAuthMethodName($this->fields["auth_method"], $this->fields["id_auth"], 1);
883

    
884
                                                echo "</td><td align='center' colspan='2'></td>";
885
                                                echo "</tr>";
886
                                        }
887
                                        
888
                                        echo "<tr class='tab_bg_1' align='center'><td>" . $LANG["login"][24] . ":</td><td class='center'>";
889
                                        if ($this->fields["date_mod"] != "0000-00-00 00:00:00"){
890
                                                echo convDateTime($this->fields["date_mod"]);
891
                                        }
892
                                        echo "</td><td>" . $LANG["login"][0] . ":</td><td>";
893

    
894
                                        if ($this->fields["last_login"] != "0000-00-00 00:00:00"){
895
                                                echo convDateTime($this->fields["last_login"]);
896
                                        }
897

    
898
                                        echo "</td>";
899
                                        echo "</tr>";
900

    
901
                                }
902
                                if ($use_cache){
903
                                        $CFG_GLPI["cache"]->end();
904
                                }
905
                        }
906

    
907
                        if ($canedit){
908
                                if ($this->fields["name"] == "") {
909
                                        echo "<tr>";
910
                                        echo "<td class='tab_bg_2' valign='top' colspan='4' align='center'>";
911
                                        echo "<input type='submit' name='add' value=\"" . $LANG["buttons"][8] . "\" class='submit'>";
912
                                        echo "</td>";
913
                                        echo "</tr>";
914
                                } else {
915
                                        echo "<tr>";
916
                                        echo "<td class='tab_bg_2' valign='top' align='center' colspan='2'>";
917
                                        echo "<input type='submit' name='update' value=\"" . $LANG["buttons"][7] . "\" class='submit' >";
918
                                        echo "</td>";
919
                                        echo "<td class='tab_bg_2' valign='top' align='center' colspan='2'>\n";
920
                                        if (!$this->fields["deleted"]){
921
                                                echo "<input type='submit' name='delete' onclick=\"return confirm('" . $LANG["common"][50] . "')\" value=\"".$LANG["buttons"][6]."\" class='submit'>";
922
                                         }else {
923
                                                echo "<input type='submit' name='restore' value=\"".$LANG["buttons"][21]."\" class='submit'>";
924

    
925
                                                echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input type='submit' name='purge' value=\"".$LANG["buttons"][22]."\" class='submit'>";
926
                                        }
927

    
928
                                        echo "</td>";
929
                                        echo "</tr>";
930
                                }
931
                        }
932
                        echo "</table></form></div>";
933
                        return true;
934
                } else {
935
                        echo "<div class='center'><strong>".$LANG["common"][54]."</strong></div>";
936
                        return false;
937
                }
938
        }
939

    
940
        /**
941
         * Print the user preference form
942
         *
943
         *@param $target form target
944
         *@param $ID Integer : Id of the user
945
         *
946
         *@return boolean : user found
947
         **/
948
        function showMyForm($target, $ID) {
949

    
950
                // Affiche un formulaire User
951
                global $CFG_GLPI, $LANG,$PLUGIN_HOOKS;
952

    
953
                if ($ID != $_SESSION["glpiID"])
954
                        return false;
955

    
956
                if ($this->getFromDB($ID)) {
957
                        //$this->showOnglets($ID, $withtemplate,$_SESSION['glpi_onglet']);
958
                        $auth_method = $this->getAuthMethodsByID();
959

    
960
                        $extauth = ! ($this->fields["auth_method"]==AUTH_DB_GLPI 
961
                                || ($this->fields["auth_method"]==NOT_YET_AUTHENTIFIED 
962
                                                && (!empty ($this->fields["password"]) || !empty ($this->fields["password_md5"])))
963
                                );
964

    
965
                        // No autocopletion : 
966
                        $save_autocompletion=$CFG_GLPI["ajax_autocompletion"];
967
                        $CFG_GLPI["ajax_autocompletion"]=false;
968

    
969
                        
970
                        echo "<div class='center'>";
971
                        echo "<form method='post' name=\"user_manager\" action=\"$target\"><table class='tab_cadre_fixe'>";
972
                        echo "<tr><th colspan='2'>" . $LANG["common"][34] . " : " . $this->fields["name"] . "</th></tr>";
973

    
974
                        echo "<tr class='tab_bg_1'>";
975
                        echo "<td class='center'>" . $LANG["setup"][18] . "</td>";
976
                        echo "<td class='center'><strong>" . $this->fields["name"] . "</strong>";
977
                        echo "<input type='hidden' name='name' value=\"" . $this->fields["name"] . "\">";
978
                        echo "<input type='hidden' name='ID' value=\"" . $this->fields["ID"] . "\">";
979
                        echo "</td></tr>";
980

    
981
                        //do some rights verification
982
                        if (!$extauth && haveRight("password_update", "1")) {
983
                                echo "<tr class='tab_bg_1'><td class='center'>" . $LANG["setup"][19] . "</td><td><input type='password' name='password' value='' size='30' /></td></tr>";
984
                        }
985

    
986
                        if ($CFG_GLPI["debug"] != DEMO_MODE || haveRight("config", 1)) {
987
                                echo "<tr class='tab_bg_1'><td class='center'>" . $LANG["setup"][41] . "</td><td>";
988
                                dropdownLanguages("language", $_SESSION["glpilanguage"]);
989
                                echo "</td></tr>";
990
                        }
991

    
992
                        echo "<tr class='tab_bg_1'><td class='center'>" . $LANG["common"][48] . "</td><td>";
993
                        if ($extauth && isset ($auth_method['ldap_field_email']) && !empty ($auth_method['ldap_field_realname'])) {
994
                                echo $this->fields["realname"];
995
                        } else {
996
                                autocompletionTextField("realname", "glpi_users", "realname", $this->fields["realname"], 30);
997
                        }
998
                        echo "</td></tr>";
999

    
1000
                        echo "<tr class='tab_bg_1'><td class='center'>" . $LANG["common"][43] . "</td><td>";
1001
                        if ($extauth && isset ($auth_method['ldap_field_firstname']) && !empty ($auth_method['ldap_field_firstname'])) {
1002
                                echo $this->fields["firstname"];
1003
                        } else {
1004
                                autocompletionTextField("firstname", "glpi_users", "firstname", $this->fields["firstname"], 30);
1005
                        }
1006
                        echo "</td></tr>";
1007

    
1008
                        echo "<tr class='tab_bg_1'><td class='center'>" . $LANG["setup"][14] . "</td><td>";
1009
                        if ($extauth && isset ($auth_method['ldap_field_email']) && !empty ($auth_method['ldap_field_email'])) {
1010
                                echo $this->fields["email"];
1011
                        } else {
1012
                                autocompletionTextField("email_form", "glpi_users", "email", $this->fields["email"], 30);
1013
                                if (!isValidEmail($this->fields["email"])){
1014
                                        echo "<span class='red'>".$LANG["mailing"][110]."</span>";
1015
                                }
1016
                        }
1017

    
1018
                        echo "</td></tr>";
1019

    
1020
                        echo "<tr class='tab_bg_1'><td class='center'>" . $LANG["help"][35] . "</td><td>";
1021
                        if ($extauth && isset ($auth_method['ldap_field_phone']) && !empty ($auth_method['ldap_field_phone'])) {
1022
                                echo $this->fields["phone"];
1023
                        } else {
1024
                                autocompletionTextField("phone", "glpi_users", "phone", $this->fields["phone"], 30);
1025
                        }
1026
                        echo "</td></tr>";
1027

    
1028
                        echo "<tr class='tab_bg_1'><td class='center'>" . $LANG["help"][35] . " 2</td><td>";
1029
                        if ($extauth && isset ($auth_method['ldap_field_phone2']) && !empty ($auth_method['ldap_field_phone2'])) {
1030
                                echo $this->fields["phone2"];
1031
                        } else {
1032
                                autocompletionTextField("phone2", "glpi_users", "phone2", $this->fields["phone2"], 30);
1033
                        }
1034
                        echo "</td></tr>";
1035

    
1036
                        echo "<tr class='tab_bg_1'><td class='center'>" . $LANG["common"][42] . "</td><td>";
1037
                        if ($extauth && isset ($auth_method['ldap_field_mobile']) && !empty ($auth_method['ldap_field_mobile'])) {
1038
                                echo $this->fields["mobile"];
1039
                        } else {
1040
                                autocompletionTextField("mobile", "glpi_users", "mobile", $this->fields["mobile"], 30);
1041
                        }
1042
                        echo "</td></tr>";
1043

    
1044
                        echo "<tr class='tab_bg_1'><td class='center'>" . $LANG["setup"][40] . "</td><td>";
1045
                        dropdownYesNo('tracking_order',$_SESSION["glpitracking_order"]);
1046
                        echo "</td></tr>";
1047

    
1048
                        echo "<tr class='tab_bg_1'><td class='center'>" . $LANG["setup"][111] . "</td><td>";
1049
                        dropdownInteger("list_limit", $this->fields["list_limit"],5,$CFG_GLPI['list_limit_max'],5);
1050
                        echo "</td></tr>";
1051

    
1052
                        if (count($_SESSION['glpiprofiles'])>1){
1053
                                echo "<tr class='tab_bg_1'><td class='center'>" . $LANG["profiles"][13] . "</td><td>";
1054
                                $options=array(0=>'----');
1055
                
1056
                                foreach ($_SESSION['glpiprofiles'] as $prof){
1057
                                        $options[$prof['ID']]=$prof['name'];
1058
                                }
1059
                                dropdownArrayValues("FK_profiles",$options,$this->fields["FK_profiles"]);
1060
                                echo "</td></tr>";
1061
                        }
1062

    
1063
                        if (count($_SESSION['glpiactiveentities'])>1){
1064
                                echo "<tr class='tab_bg_1'><td class='center'>" . $LANG["profiles"][37] . "</td><td>";
1065
                                dropdownValue("glpi_entities","FK_entities",$_SESSION["glpidefault_entity"],1,$_SESSION['glpiactiveentities']);
1066
                                echo "</td></tr>";
1067
                        }
1068
                        
1069
                        echo "<tr>";
1070
                        echo "<td class='tab_bg_2' valign='top' align='center' colspan='2'>";
1071
                        echo "<input type='submit' name='update' value=\"" . $LANG["buttons"][7] . "\" class='submit' >";
1072
                        echo "</td>";
1073
                        echo "</tr>";
1074

    
1075
                        echo "</table></form></div>";
1076
                        $CFG_GLPI["ajax_autocompletion"]=$save_autocompletion;
1077
                        return true;
1078
                }
1079
                return false;
1080
        }
1081

    
1082
        ///Get all the authentication method parameters for the current user
1083
        function getAuthMethodsByID() {
1084
                return getAuthMethodsByID($this->fields["auth_method"], $this->fields["id_auth"]);
1085
        }
1086

    
1087
        function pre_updateInDB($input,$updates) {
1088
                
1089
                // Security system except for login update
1090
                if (isset ($_SESSION["glpiID"]) && !haveRight("user", "w") && !ereg("login.php", $_SERVER['PHP_SELF'])) { 
1091
                        if ($_SESSION["glpiID"] == $input['ID']) { 
1092
                                $ret = $updates;
1093
                                
1094
                                if (isset($this->fields["auth_method"])){
1095
                                        // extauth ldap case 
1096
                                        if ($_SESSION["glpiextauth"] && ($this->fields["auth_method"] == AUTH_LDAP || isAlternateAuthWithLdap($this->fields["auth_method"]))) {
1097
                                                $auth_method = getAuthMethodsByID($this->fields["auth_method"], $this->fields["id_auth"]);
1098
                                                if (count($auth_method)){
1099
                                                        $fields=getLDAPSyncFields($auth_method);
1100
                                                        foreach ($fields as $key => $val){ 
1101
                                                                if (!empty ($val)){
1102
                                                                        unset ($ret[$key]);
1103
                                                                }
1104
                                                        }
1105
                                                }
1106
                                        }
1107
                                        // extauth imap case
1108
                                        if (isset($this->fields["auth_method"])&&$this->fields["auth_method"] == AUTH_MAIL){
1109
                                                unset ($ret["email"]);
1110
                                        }
1111
                                        
1112
                                        unset ($ret["active"]);
1113
                                        unset ($ret["comments"]);
1114
                                }
1115
                                
1116
                                return array($input,$ret); 
1117
                        } else { 
1118
                                return array($input,array());
1119
                        }
1120
                }
1121
                
1122
                return array($input,$updates);
1123
        }
1124

    
1125

    
1126
        /**
1127
         * Delete dynamic profiles for the current user
1128
         **/
1129
        function purgeDynamicProfiles()
1130
        {
1131
                global $DB;
1132
                
1133
                //Purge only in case of connection to the master mysql server
1134
                if (!$DB->isSlave())
1135
                {
1136
                        $sql = "DELETE FROM glpi_users_profiles WHERE FK_users='".$this->fields["ID"]."' AND dynamic=1";
1137
                        $DB->query($sql);
1138
                }
1139
        }
1140
}
1141

    
1142

    
1143
?>
Redmine Appliance - Powered by TurnKey Linux