authldap.class.php 143 KB
Newer Older
yllen's avatar
CS  
yllen committed
1
<?php
2 3 4
/**
 * ---------------------------------------------------------------------
 * GLPI - Gestionnaire Libre de Parc Informatique
5
 * Copyright (C) 2015-2018 Teclib' and contributors.
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
 *
 * http://glpi-project.org
 *
 * based on GLPI - Gestionnaire Libre de Parc Informatique
 * Copyright (C) 2003-2014 by the INDEPNET Development Team.
 *
 * ---------------------------------------------------------------------
 *
 * LICENSE
 *
 * This file is part of GLPI.
 *
 * GLPI is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * GLPI is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with GLPI. If not, see <http://www.gnu.org/licenses/>.
 * ---------------------------------------------------------------------
moyooo's avatar
moyooo committed
31 32 33 34
 */

/**
 *  Class used to manage Auth LDAP config
Anael Mobilia's avatar
PHPdoc  
Anael Mobilia committed
35
 */
moyooo's avatar
moyooo committed
36 37
class AuthLDAP extends CommonDBTM {

38 39
   const SIMPLE_INTERFACE = 'simple';
   const EXPERT_INTERFACE = 'expert';
Walid Nouh's avatar
Walid Nouh committed
40

yllen's avatar
yllen committed
41
   const ACTION_IMPORT      = 0;
Walid Nouh's avatar
Walid Nouh committed
42
   const ACTION_SYNCHRONIZE = 1;
yllen's avatar
yllen committed
43
   const ACTION_ALL         = 2;
Walid Nouh's avatar
Walid Nouh committed
44

yllen's avatar
yllen committed
45
   const USER_IMPORTED     = 0;
46 47 48
   const USER_SYNCHRONIZED = 1;
   const USER_DELETED_LDAP = 2;

49 50 51 52 53 54
   //Import user by giving his login
   const IDENTIFIER_LOGIN = 'login';

   //Import user by giving his email
   const IDENTIFIER_EMAIL = 'email';

Johan Cwiklinski's avatar
Johan Cwiklinski committed
55 56 57 58
   const GROUP_SEARCH_USER    = 0;
   const GROUP_SEARCH_GROUP   = 1;
   const GROUP_SEARCH_BOTH    = 2;

Cédric Anne's avatar
Cédric Anne committed
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
   /**
    * Deleted user strategy: preserve user.
    * @var integer
    */
   const DELETED_USER_PRESERVE = 0;

   /**
    * Deleted user strategy: put user in trashbin.
    * @var integer
    */
   const DELETED_USER_DELETE = 1;

   /**
    * Deleted user strategy: withdraw dynamic authorizations and groups.
    * @var integer
    */
   const DELETED_USER_WITHDRAWDYNINFO = 2;

   /**
    * Deleted user strategy: disable user.
    * @var integer
    */
   const DELETED_USER_DISABLE = 3;

   /**
    * Deleted user strategy: disable user and withdraw dynamic authorizations and groups.
    * @var integer
    */
   const DELETED_USER_DISABLEANDWITHDRAWDYNINFO = 4;

Walid Nouh's avatar
Walid Nouh committed
89 90
   // From CommonDBTM
   public $dohistory = true;
91

moyooo's avatar
moyooo committed
92 93
   static $rightname = 'config';

94 95 96
   //connection caching stuff
   static $conn_cache = [];

Anael Mobilia's avatar
PHPdoc  
Anael Mobilia committed
97
   static function getTypeName($nb = 0) {
yllen's avatar
yllen committed
98
      return _n('LDAP directory', 'LDAP directories', $nb);
99 100
   }

101
   static function canCreate() {
moyooo's avatar
moyooo committed
102
      return static::canUpdate();
103 104
   }

moyooo's avatar
moyooo committed
105 106
   static function canPurge() {
      return static::canUpdate();
107 108
   }

yllen's avatar
CS  
yllen committed
109
   function post_getEmpty() {
yllen's avatar
yllen committed
110

yllen's avatar
yllen committed
111 112 113
      $this->fields['port']                        = '389';
      $this->fields['condition']                   = '';
      $this->fields['login_field']                 = 'uid';
Johan Cwiklinski's avatar
Johan Cwiklinski committed
114
      $this->fields['sync_field']                  = null;
yllen's avatar
yllen committed
115 116 117
      $this->fields['use_tls']                     = 0;
      $this->fields['group_field']                 = '';
      $this->fields['group_condition']             = '';
Johan Cwiklinski's avatar
Johan Cwiklinski committed
118
      $this->fields['group_search_type']           = self::GROUP_SEARCH_USER;
yllen's avatar
yllen committed
119
      $this->fields['group_member_field']          = '';
yllen's avatar
yllen committed
120 121 122 123
      $this->fields['email1_field']                = 'mail';
      $this->fields['email2_field']                = '';
      $this->fields['email3_field']                = '';
      $this->fields['email4_field']                = '';
moyooo's avatar
moyooo committed
124
      $this->fields['realname_field']              = 'sn';
yllen's avatar
yllen committed
125 126 127 128 129 130 131 132
      $this->fields['firstname_field']             = 'givenname';
      $this->fields['phone_field']                 = 'telephonenumber';
      $this->fields['phone2_field']                = '';
      $this->fields['mobile_field']                = '';
      $this->fields['registration_number_field']   = '';
      $this->fields['comment_field']               = '';
      $this->fields['title_field']                 = '';
      $this->fields['use_dn']                      = 0;
moyooo's avatar
moyooo committed
133
      $this->fields['picture_field']               = '';
134
      $this->fields['responsible_field']           = '';
moyooo's avatar
moyooo committed
135 136
   }

137 138
   static public function unsetUndisclosedFields(&$fields) {
      unset($fields['rootdn_passwd']);
139 140
   }

moyooo's avatar
moyooo committed
141 142
   /**
    * Preconfig datas for standard system
yllen's avatar
yllen committed
143
    *
Anael Mobilia's avatar
PHPdoc  
Anael Mobilia committed
144
    * @param string $type type of standard system : AD
yllen's avatar
yllen committed
145
    *
Anael Mobilia's avatar
PHPdoc  
Anael Mobilia committed
146 147
    * @return void
    */
moyooo's avatar
moyooo committed
148
   function preconfig($type) {
yllen's avatar
yllen committed
149

Anael Mobilia's avatar
Anael Mobilia committed
150
      switch ($type) {
moyooo's avatar
moyooo committed
151
         case 'AD' :
yllen's avatar
yllen committed
152
            $this->fields['port']                      = "389";
yllen's avatar
yllen committed
153 154
            $this->fields['condition']
               = '(&(objectClass=user)(objectCategory=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))';
yllen's avatar
yllen committed
155
            $this->fields['login_field']               = 'samaccountname';
Johan Cwiklinski's avatar
Johan Cwiklinski committed
156
            $this->fields['sync_field']                = 'objectguid';
yllen's avatar
yllen committed
157 158
            $this->fields['use_tls']                   = 0;
            $this->fields['group_field']               = 'memberof';
yllen's avatar
yllen committed
159 160
            $this->fields['group_condition']
               = '(&(objectClass=user)(objectCategory=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))';
Johan Cwiklinski's avatar
Johan Cwiklinski committed
161
            $this->fields['group_search_type']         = self::GROUP_SEARCH_USER;
yllen's avatar
yllen committed
162
            $this->fields['group_member_field']        = '';
yllen's avatar
yllen committed
163 164 165 166
            $this->fields['email1_field']              = 'mail';
            $this->fields['email2_field']              = '';
            $this->fields['email3_field']              = '';
            $this->fields['email4_field']              = '';
yllen's avatar
yllen committed
167 168 169 170 171
            $this->fields['realname_field']            = 'sn';
            $this->fields['firstname_field']           = 'givenname';
            $this->fields['phone_field']               = 'telephonenumber';
            $this->fields['phone2_field']              = 'othertelephone';
            $this->fields['mobile_field']              = 'mobile';
172
            $this->fields['registration_number_field'] = 'employeenumber';
yllen's avatar
yllen committed
173 174 175 176
            $this->fields['comment_field']             = 'info';
            $this->fields['title_field']               = 'title';
            $this->fields['entity_field']              = 'ou';
            $this->fields['entity_condition']          = '(objectclass=organizationalUnit)';
177 178
            $this->fields['use_dn']                    = 1;
            $this->fields['can_support_pagesize']      = 1;
179
            $this->fields['pagesize']                  = '1000';
moyooo's avatar
moyooo committed
180
            $this->fields['picture_field']             = '';
181
            $this->fields['responsible_field']         = 'manager';
moyooo's avatar
moyooo committed
182 183 184 185 186 187
            break;

         default:
            $this->post_getEmpty();
      }
   }
yllen's avatar
yllen committed
188

moyooo's avatar
moyooo committed
189
   function prepareInputForUpdate($input) {
yllen's avatar
yllen committed
190

yllen's avatar
yllen committed
191 192 193
      if (isset($input["rootdn_passwd"])) {
         if (empty($input["rootdn_passwd"])) {
            unset($input["rootdn_passwd"]);
moyooo's avatar
moyooo committed
194
         } else {
yllen's avatar
yllen committed
195 196
            $input["rootdn_passwd"] = Toolbox::encrypt(stripslashes($input["rootdn_passwd"]),
                                                       GLPIKEY);
moyooo's avatar
moyooo committed
197
         }
moyooo's avatar
moyooo committed
198
      }
199

200
      if (isset($input["_blank_passwd"]) && $input["_blank_passwd"]) {
moyooo's avatar
moyooo committed
201 202 203
         $input['rootdn_passwd'] = '';
      }

204 205 206
      // Set attributes in lower case
      if (count($input)) {
         foreach ($input as $key => $val) {
207
            if (preg_match('/_field$/', $key)) {
yllen's avatar
yllen committed
208
               $input[$key] = Toolbox::strtolower($val);
209 210 211
            }
         }
      }
Johan Cwiklinski's avatar
Johan Cwiklinski committed
212 213 214 215 216 217 218 219 220 221

      //do not permit to override sync_field
      if ($this->isSyncFieldEnabled()
         && isset($input['sync_field'])
         && $this->isSyncFieldUsed()
      ) {
         if ($input['sync_field'] == $this->fields['sync_field']) {
            unset($input['sync_field']);
         } else {
            Session::addMessageAfterRedirect(
222
               __('Synchronization field cannot be changed once in use.'),
Johan Cwiklinski's avatar
Johan Cwiklinski committed
223 224 225 226 227 228
               false,
               ERROR
            );
            return false;
         };
      }
moyooo's avatar
moyooo committed
229 230
      return $input;
   }
yllen's avatar
CS  
yllen committed
231

232
   static function getSpecificValueToDisplay($field, $values, array $options = []) {
moyooo's avatar
moyooo committed
233 234

      if (!is_array($values)) {
235
         $values = [$field => $values];
moyooo's avatar
moyooo committed
236 237 238 239 240 241 242 243
      }
      switch ($field) {
         case 'group_search_type' :
            return self::getGroupSearchTypeName($values[$field]);
      }
      return parent::getSpecificValueToDisplay($field, $values, $options);
   }

244
   static function getSpecificValueToSelect($field, $name = '', $values = '', array $options = []) {
yllen's avatar
CS  
yllen committed
245

moyooo's avatar
moyooo committed
246
      if (!is_array($values)) {
247
         $values = [$field => $values];
moyooo's avatar
moyooo committed
248 249 250 251 252 253 254 255 256 257 258
      }
      $options['display'] = false;
      switch ($field) {
         case 'group_search_type' :
            $options['value'] = $values[$field];
            $options['name']  = $name;
            return self::dropdownGroupSearchType($options);
      }
      return parent::getSpecificValueToSelect($field, $name, $values, $options);
   }

Anael Mobilia's avatar
PHPdoc  
Anael Mobilia committed
259
   static function processMassiveActionsForOneItemtype(MassiveAction $ma, CommonDBTM $item, array $ids) {
moyooo's avatar
moyooo committed
260 261 262
      $input = $ma->getInput();

      switch ($ma->getAction()) {
yllen's avatar
CS  
yllen committed
263
         case 'import_group' :
moyooo's avatar
moyooo committed
264
            $group = new Group;
yllen's avatar
CS  
yllen committed
265 266
            if (!Session::haveRight("user", User::UPDATEAUTHENT)
                || !$group->canGlobal(UPDATE)) {
moyooo's avatar
moyooo committed
267 268
               $ma->itemDone($item->getType(), $ids, MassiveAction::ACTION_NORIGHT);
               $ma->addMessage($item->getErrorMessage(ERROR_RIGHT));
269
               return;
moyooo's avatar
moyooo committed
270 271 272 273 274 275 276 277 278 279 280 281
            }
            foreach ($ids as $id) {
               if (isset($input["dn"][$id])) {
                  $group_dn = $input["dn"][$id];
                  if (isset($input["ldap_import_entities"][$id])) {
                     $entity = $input["ldap_import_entities"][$id];
                  } else {
                     $entity = $_SESSION["glpiactive_entity"];
                  }
                  // Is recursive is in the main form and thus, don't pass through
                  // zero_on_empty mechanism inside massive action form ...
                  $is_recursive = (empty($input['ldap_import_recursive'][$id]) ? 0 : 1);
282
                  $options      = ['authldaps_id' => $_SESSION['ldap_server'],
yllen's avatar
CS  
yllen committed
283 284
                                        'entities_id'  => $entity,
                                        'is_recursive' => $is_recursive,
285
                                        'type'         => $input['ldap_import_type'][$id]];
286
                  if (AuthLdap::ldapImportGroup($group_dn, $options)) {
moyooo's avatar
moyooo committed
287
                     $ma->itemDone($item->getType(), $id, MassiveAction::ACTION_OK);
Anael Mobilia's avatar
Anael Mobilia committed
288
                  } else {
moyooo's avatar
moyooo committed
289
                     $ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO);
290
                     $ma->addMessage($item->getErrorMessage(ERROR_ON_ACTION, $group_dn));
moyooo's avatar
moyooo committed
291 292
                  }
               }
293
               // Clean history as id does not correspond to group
294
               $_SESSION['glpimassiveactionselected'] = [];
moyooo's avatar
moyooo committed
295 296 297
            }
            return;

298 299 300 301 302 303 304 305
         case 'import' :
         case 'sync' :
            if (!Session::haveRight("user", User::IMPORTEXTAUTHUSERS)) {
               $ma->itemDone($item->getType(), $ids, MassiveAction::ACTION_NORIGHT);
               $ma->addMessage($item->getErrorMessage(ERROR_RIGHT));
               return;
            }
            foreach ($ids as $id) {
306
               if (AuthLdap::ldapImportUserByServerId(['method' => AuthLDAP::IDENTIFIER_LOGIN,
Johan Cwiklinski's avatar
Johan Cwiklinski committed
307
                                                       'value'  => $id],
308
                                                      $_SESSION['ldap_import']['mode'],
yllen's avatar
CS  
yllen committed
309 310
                                                      $_SESSION['ldap_import']['authldaps_id'],
                                                      true)) {
311
                  $ma->itemDone($item->getType(), $id, MassiveAction::ACTION_OK);
Anael Mobilia's avatar
Anael Mobilia committed
312
               } else {
313 314
                  $ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO);
                  $ma->addMessage($item->getErrorMessage(ERROR_ON_ACTION, $id));
315 316
               }
            }
317
            return;
318
      }
319 320

      parent::processMassiveActionsForOneItemtype($ma, $item, $ids);
321
   }
yllen's avatar
CS  
yllen committed
322

moyooo's avatar
moyooo committed
323
   /**
yllen's avatar
yllen committed
324 325
    * Print the auth ldap form
    *
Anael Mobilia's avatar
PHPdoc  
Anael Mobilia committed
326 327
    * @param integer $ID      ID of the item
    * @param array   $options Options
yllen's avatar
yllen committed
328
    *     - target for the form
yllen's avatar
yllen committed
329
    *
Anael Mobilia's avatar
PHPdoc  
Anael Mobilia committed
330 331
    * @return void (display)
    */
332
   function showForm($ID, $options = []) {
moyooo's avatar
moyooo committed
333

moyooo's avatar
moyooo committed
334
      if (!Config::canUpdate()) {
moyooo's avatar
moyooo committed
335 336
         return false;
      }
yllen's avatar
yllen committed
337
      if (empty($ID)) {
Cédric Anne's avatar
Cédric Anne committed
338
         $this->getEmpty();
339 340
         if (isset($options['preconfig'])) {
            $this->preconfig($options['preconfig']);
moyooo's avatar
moyooo committed
341 342
         }
      } else {
Cédric Anne's avatar
Cédric Anne committed
343
         $this->getFromDB($ID);
moyooo's avatar
moyooo committed
344 345
      }

yllen's avatar
yllen committed
346
      if (Toolbox::canUseLdap()) {
347
         $this->showFormHeader($options);
moyooo's avatar
moyooo committed
348
         if (empty($ID)) {
moyooo's avatar
moyooo committed
349
            $target = $this->getFormURL();
moyooo's avatar
moyooo committed
350
            echo "<tr class='tab_bg_2'><td>".__('Preconfiguration')."</td> ";
moyooo's avatar
moyooo committed
351
            echo "<td colspan='3'>";
moyooo's avatar
moyooo committed
352
            echo "<a href='$target?preconfig=AD'>".__('Active Directory')."</a>";
moyooo's avatar
moyooo committed
353
            echo "&nbsp;&nbsp;/&nbsp;&nbsp;";
remi's avatar
gettext  
remi committed
354
            echo "<a href='$target?preconfig=default'>".__('Default values');
moyooo's avatar
moyooo committed
355 356
            echo "</a></td></tr>";
         }
Johan Cwiklinski's avatar
Johan Cwiklinski committed
357 358
         echo "<tr class='tab_bg_1'><td><label for='name'>" . __('Name') . "</label></td>";
         echo "<td><input type='text' id='name' name='name' value='". $this->fields["name"] ."'></td>";
yllen's avatar
yllen committed
359 360
         if ($ID > 0) {
            echo "<td>".__('Last update')."</td><td>".Html::convDateTime($this->fields["date_mod"]);
361 362 363
         } else {
            echo "<td colspan='2'>&nbsp;";
         }
moyooo's avatar
moyooo committed
364 365
         echo "</td></tr>";

Johan Cwiklinski's avatar
Johan Cwiklinski committed
366 367
         $defaultrand = mt_rand();
         echo "<tr class='tab_bg_1'><td><label for='dropdown_is_default$defaultrand'>" . __('Default server') . "</label></td>";
368
         echo "<td>";
Johan Cwiklinski's avatar
Johan Cwiklinski committed
369
         Dropdown::showYesNo('is_default', $this->fields['is_default'], -1, ['rand' => $defaultrand]);
370
         echo "</td>";
Johan Cwiklinski's avatar
Johan Cwiklinski committed
371 372
         $activerand = mt_rand();
         echo "<td><label for='dropdown_is_active$activerand'>" . __('Active'). "</label></td>";
373
         echo "<td>";
Johan Cwiklinski's avatar
Johan Cwiklinski committed
374
         Dropdown::showYesNo('is_active', $this->fields['is_active'], -1, ['rand' => $activerand]);
yllen's avatar
yllen committed
375
         echo "</td></tr>";
376

Johan Cwiklinski's avatar
Johan Cwiklinski committed
377 378 379 380
         echo "<tr class='tab_bg_1'><td><label for='host'>" . __('Server') . "</label></td>";
         echo "<td><input type='text' id='host' name='host' value='" . $this->fields["host"] . "'></td>";
         echo "<td><label for='port'>" . __('Port (default=389)') . "</label></td>";
         echo "<td><input id='port' type='text' id='port' name='port' value='".$this->fields["port"]."'>";
moyooo's avatar
moyooo committed
381 382
         echo "</td></tr>";

Johan Cwiklinski's avatar
Johan Cwiklinski committed
383
         echo "<tr class='tab_bg_1'><td><label for='condition'>" . __('Connection filter') . "</label></td>";
yllen's avatar
yllen committed
384
         echo "<td colspan='3'>";
Johan Cwiklinski's avatar
Johan Cwiklinski committed
385
         echo "<textarea cols='100' rows='1' id='condition' name='condition'>".$this->fields["condition"];
yllen's avatar
CS  
yllen committed
386
         echo "</textarea>";
yllen's avatar
yllen committed
387
         echo "</td></tr>";
Walid Nouh's avatar
Walid Nouh committed
388

Johan Cwiklinski's avatar
Johan Cwiklinski committed
389
         echo "<tr class='tab_bg_1'><td><label for='basedn'>" . __('BaseDN') . "</label></td>";
yllen's avatar
yllen committed
390
         echo "<td colspan='3'>";
Johan Cwiklinski's avatar
Johan Cwiklinski committed
391
         echo "<input type='text' id='basedn' name='basedn' size='100' value=\"".$this->fields["basedn"]."\">";
Walid Nouh's avatar
Walid Nouh committed
392 393
         echo "</td></tr>";

Johan Cwiklinski's avatar
Johan Cwiklinski committed
394 395
         echo "<tr class='tab_bg_1'><td><label for='rootdn'>" . __('RootDN (for non anonymous binds)') . "</label></td>";
         echo "<td colspan='3'><input type='text' name='rootdn' id='rootdn' size='100' value=\"".
moyooo's avatar
moyooo committed
396
                $this->fields["rootdn"]."\">";
moyooo's avatar
moyooo committed
397 398
         echo "</td></tr>";

Johan Cwiklinski's avatar
Johan Cwiklinski committed
399 400 401
         echo "<tr class='tab_bg_1'><td><label for='rootdn_passwd'>" .
            __('Password (for non-anonymous binds)') . "</label></td>";
         echo "<td><input type='password' id='rootdn_passwd' name='rootdn_passwd' value='' autocomplete='off'>";
moyooo's avatar
moyooo committed
402
         if ($ID) {
Johan Cwiklinski's avatar
Johan Cwiklinski committed
403 404
            echo "<input type='checkbox' name='_blank_passwd' id='_blank_passwd'>&nbsp;"
               . "<label for='_blank_passwd'>" . __('Clear') . "</label>";
moyooo's avatar
moyooo committed
405 406
         }
         echo "</td>";
Johan Cwiklinski's avatar
Johan Cwiklinski committed
407 408 409
         echo "<td rowspan='3'><label for='comment'>".__('Comments')."</label></td>";
         echo "<td rowspan='3' class='middle'>";
         echo "<textarea cols='40' rows='4' name='comment' id='comment'>".$this->fields["comment"]."</textarea>";
moyooo's avatar
moyooo committed
410 411
         echo "</td></tr>";

Johan Cwiklinski's avatar
Johan Cwiklinski committed
412 413 414 415 416
         echo "<tr class='tab_bg_1'>";
         echo "<td><label for='login_field'>" . __('Login field') . "</label></td>";
         echo "<td><input type='text' id='login_field' name='login_field' value='".$this->fields["login_field"]."'>";
         echo "</td></tr>";

Johan Cwiklinski's avatar
Johan Cwiklinski committed
417
         $info_message = __s('Synchronization field cannot be changed once in use.');
Johan Cwiklinski's avatar
Johan Cwiklinski committed
418 419 420 421 422 423 424 425
         echo "<tr class='tab_bg_1'>";
         echo "<td><label for='sync_field'>" . __('Synchronization field') . "<i class='pointer fa fa-info' title='$info_message'></i></td>";
         echo "<td><input type='text' id='sync_field' name='sync_field' value='{$this->fields["sync_field"]}' title='$info_message'";
         if ($this->isSyncFieldEnabled() && $this->isSyncFieldUsed()) {
            echo " disabled='disabled'";
         }
         echo ">";
         echo "</td></tr>";
moyooo's avatar
moyooo committed
426

moyooo's avatar
moyooo committed
427 428
         //Fill fields when using preconfiguration models
         if (!$ID) {
429
            $hidden_fields = ['comment_field', 'condition', 'email1_field', 'email2_field',
430
                                   'email3_field', 'email4_field', 'entity_condition',
yllen's avatar
yllen committed
431 432
                                   'entity_field', 'firstname_field', 'group_condition',
                                   'group_field', 'group_member_field', 'group_search_type',
yllen's avatar
r13654  
yllen committed
433 434
                                   'mobile_field', 'phone_field', 'phone2_field', 'port',
                                   'realname_field', 'registration_number_field', 'title_field',
435
                                   'use_dn', 'use_tls', 'responsible_field'];
moyooo's avatar
moyooo committed
436 437

            foreach ($hidden_fields as $hidden_field) {
yllen's avatar
yllen committed
438 439
               echo "<input type='hidden' name='$hidden_field' value='".
                      $this->fields[$hidden_field]."'>";
moyooo's avatar
moyooo committed
440 441 442
            }
         }

moyooo's avatar
moyooo committed
443 444 445
         echo "</td></tr>";

         $this->showFormButtons($options);
yllen's avatar
yllen committed
446

moyooo's avatar
moyooo committed
447 448
      } else {
         echo "<div class='center'>&nbsp;<table class='tab_cadre_fixe'>";
yllen's avatar
yllen committed
449
         echo "<tr><th colspan='2'>" . self::getTypeName(1) . "</th></tr>";
moyooo's avatar
moyooo committed
450
         echo "<tr class='tab_bg_2'><td class='center'>";
451
         echo "<p class='red'>".sprintf(__('%s extension is missing'), 'LDAP')."</p>";
yllen's avatar
yllen committed
452
         echo "<p>".__('Impossible to use LDAP as external source of connection')."</p>".
453 454 455 456 457
              "</td></tr></table>";

         echo "<p><strong>".GLPINetwork::getErrorMessage()."</strong></p>";
         echo "</div>";

moyooo's avatar
moyooo committed
458 459 460
      }
   }

Anael Mobilia's avatar
PHPdoc  
Anael Mobilia committed
461 462 463 464 465
   /**
    * Show advanced config form
    *
    * @return void
    */
yllen's avatar
yllen committed
466
   function showFormAdvancedConfig() {
moyooo's avatar
moyooo committed
467

yllen's avatar
yllen committed
468
      $ID = $this->getField('id');
moyooo's avatar
end w3c  
moyooo committed
469
      $hidden = '';
yllen's avatar
yllen committed
470

moyooo's avatar
end w3c  
moyooo committed
471
      echo "<div class='center'>";
yllen's avatar
yllen committed
472
      echo "<form method='post' action='".Toolbox::getItemTypeFormURL(__CLASS__)."'>";
moyooo's avatar
end w3c  
moyooo committed
473
      echo "<table class='tab_cadre_fixe'>";
moyooo's avatar
moyooo committed
474 475

      echo "<tr class='tab_bg_2'><th colspan='4'>";
476
      echo "<input type='hidden' name='id' value='$ID'>". __('Advanced information')."</th></tr>";
moyooo's avatar
moyooo committed
477 478

      echo "<tr class='tab_bg_1'>";
moyooo's avatar
moyooo committed
479
      echo "<td>" . __('Use TLS') . "</td><td>";
moyooo's avatar
moyooo committed
480
      if (function_exists("ldap_start_tls")) {
yllen's avatar
yllen committed
481
         Dropdown::showYesNo('use_tls', $this->fields["use_tls"]);
moyooo's avatar
moyooo committed
482
      } else {
moyooo's avatar
moyooo committed
483
         echo "<input type='hidden' name='use_tls' value='0'>".__('ldap_start_tls does not exist');
moyooo's avatar
moyooo committed
484 485
      }
      echo "</td>";
moyooo's avatar
moyooo committed
486
      echo "<td>" . __('LDAP directory time zone') . "</td><td>";
yllen's avatar
yllen committed
487
      Dropdown::showGMT("time_offset", $this->fields["time_offset"]);
moyooo's avatar
moyooo committed
488
      echo"</td></tr>";
yllen's avatar
yllen committed
489

490
      if (self::isLdapPageSizeAvailable(false, false)) {
491 492
         echo "<tr class='tab_bg_1'>";
         echo "<td>" . __('Use paged results') . "</td><td>";
493
         Dropdown::showYesNo('can_support_pagesize', $this->fields["can_support_pagesize"]);
494
         echo "</td>";
495
         echo "<td>" . __('Page size') . "</td><td>";
496
         Dropdown::showNumber("pagesize", ['value' => $this->fields['pagesize'],
moyooo's avatar
moyooo committed
497 498
                                                'min'   => 100,
                                                'max'   => 100000,
499
                                                'step'  => 100]);
500
         echo"</td></tr>";
yllen's avatar
CS  
yllen committed
501

502 503
         echo "<tr class='tab_bg_1'>";
         echo "<td>" . __('Maximum number of results') . "</td><td>";
504
         Dropdown::showNumber('ldap_maxlimit', ['value' => $this->fields['ldap_maxlimit'],
moyooo's avatar
moyooo committed
505 506 507
                                                     'min'   => 100,
                                                     'max'   => 999999,
                                                     'step'  => 100,
508
                                                     'toadd' => [0 => __('Unlimited')]]);
509
         echo "</td><td colspan='2'></td></tr>";
yllen's avatar
CS  
yllen committed
510

511
      } else {
moyooo's avatar
end w3c  
moyooo committed
512 513 514
         $hidden .= "<input type='hidden' name='can_support_pagesize' value='0'>";
         $hidden .= "<input type='hidden' name='pagesize' value='0'>";
         $hidden .= "<input type='hidden' name='ldap_maxlimit' value='0'>";
515
      }
yllen's avatar
CS  
yllen committed
516

moyooo's avatar
moyooo committed
517
      echo "<tr class='tab_bg_1'>";
moyooo's avatar
moyooo committed
518
      echo "<td>" . __('How LDAP aliases should be handled') . "</td><td colspan='4'>";
519 520 521 522 523 524
      $alias_options = [
         LDAP_DEREF_NEVER     => __('Never dereferenced (default)'),
         LDAP_DEREF_ALWAYS    => __('Always dereferenced'),
         LDAP_DEREF_SEARCHING => __('Dereferenced during the search (but not when locating)'),
         LDAP_DEREF_FINDING   => __('Dereferenced when locating (not during the search)'),
      ];
yllen's avatar
yllen committed
525
      Dropdown::showFromArray("deref_option", $alias_options,
526
                              ['value' => $this->fields["deref_option"]]);
moyooo's avatar
moyooo committed
527
      echo"</td></tr>";
yllen's avatar
yllen committed
528

529 530 531 532 533 534
      echo "<tr class='tab_bg_1'>";
      echo "<td>".__('Domain name used by inventory tool for link the user')."</td>";
      echo "<td colspan='3'>";
      Html::autocompletionTextField($this, "inventory_domain", ['size' => 100]);
      echo "</td></tr>";

yllen's avatar
fix W3C  
yllen committed
535
      echo "<tr class='tab_bg_2'><td class='center' colspan='4'>";
536
      echo "<input type='submit' name='update' class='submit' value=\"".__s('Save')."\">";
moyooo's avatar
end w3c  
moyooo committed
537
      echo $hidden;
moyooo's avatar
moyooo committed
538
      echo "</td></tr>";
yllen's avatar
CS  
yllen committed
539

540 541 542
      echo "</table>";
      Html::closeForm();
      echo "</div>";
543

moyooo's avatar
moyooo committed
544 545
   }

Anael Mobilia's avatar
PHPdoc  
Anael Mobilia committed
546 547 548 549 550 551 552
   /**
    * Show config replicates form
    *
    * @var DBmysql $DB
    *
    * @return void
    */
yllen's avatar
yllen committed
553
   function showFormReplicatesConfig() {
yllen's avatar
yllen committed
554
      global $DB;
moyooo's avatar
moyooo committed
555

yllen's avatar
yllen committed
556 557
      $ID     = $this->getField('id');
      $target = $this->getFormURL();
moyooo's avatar
moyooo committed
558
      $rand   = mt_rand();
yllen's avatar
yllen committed
559

moyooo's avatar
moyooo committed
560 561
      AuthLdapReplicate::addNewReplicateForm($target, $ID);

Johan Cwiklinski's avatar
Johan Cwiklinski committed
562 563 564 565 566 567 568 569 570
      $iterator = $DB->request([
         'FROM'   => 'glpi_authldapreplicates',
         'WHERE'  => [
            'authldaps_id' => $ID
         ],
         'ORDER'  => ['name']
      ]);

      if (($nb = count($iterator)) > 0) {
moyooo's avatar
moyooo committed
571
         echo "<br>";
Anael Mobilia's avatar
PHPdoc  
Anael Mobilia committed
572

moyooo's avatar
moyooo committed
573
         echo "<div class='center'>";
moyooo's avatar
end w3c  
moyooo committed
574
         Html::openMassiveActionsForm('massAuthLdapReplicate'.$rand);
575 576
         $massiveactionparams = ['num_displayed' => min($_SESSION['glpilist_limit'], $nb),
                                      'container'     => 'massAuthLdapReplicate'.$rand];
moyooo's avatar
moyooo committed
577
         Html::showMassiveActions($massiveactionparams);
moyooo's avatar
moyooo committed
578
         echo "<input type='hidden' name='id' value='$ID'>";
moyooo's avatar
end w3c  
moyooo committed
579
         echo "<table class='tab_cadre_fixehov'>";
yllen's avatar
CS  
yllen committed
580 581
         echo "<tr class='noHover'>".
              "<th colspan='4'>".__('List of LDAP directory replicates') . "</th></tr>";
moyooo's avatar
moyooo committed
582 583

         if (isset($_SESSION["LDAP_TEST_MESSAGE"])) {
yllen's avatar
fix W3C  
yllen committed
584
            echo "<tr class='tab_bg_2'><td class='center' colspan='4'>";
moyooo's avatar
moyooo committed
585 586 587 588
            echo $_SESSION["LDAP_TEST_MESSAGE"];
            echo"</td></tr>";
            unset($_SESSION["LDAP_TEST_MESSAGE"]);
         }
yllen's avatar
CS  
yllen committed
589 590 591 592 593
         $header_begin   = "<tr>";
         $header_top     = "<th>".Html::getCheckAllAsCheckbox('massAuthLdapReplicate'.$rand)."</th>";
         $header_bottom  = "<th>".Html::getCheckAllAsCheckbox('massAuthLdapReplicate'.$rand)."</th>";
         $header_end     = "<th class='center b'>".__('Name')."</th>";
         $header_end    .= "<th class='center b'>"._n('Replicate', 'Replicates', 1)."</th>".
moyooo's avatar
moyooo committed
594
              "<th class='center'></th></tr>";
moyooo's avatar
moyooo committed
595
         echo $header_begin.$header_top.$header_end;
yllen's avatar
CS  
yllen committed
596

Johan Cwiklinski's avatar
Johan Cwiklinski committed
597
         while ($ldap_replicate = $iterator->next()) {
moyooo's avatar
moyooo committed
598
            echo "<tr class='tab_bg_1'><td class='center' width='10'>";
599
            Html::showMassiveActionCheckBox('AuthLdapReplicate', $ldap_replicate["id"]);
moyooo's avatar
moyooo committed
600 601
            echo "</td>";
            echo "<td class='center'>" . $ldap_replicate["name"] . "</td>";
yllen's avatar
yllen committed
602 603
            echo "<td class='center'>".sprintf(__('%1$s: %2$s'), $ldap_replicate["host"],
                                               $ldap_replicate["port"]);
604 605
            echo "</td>";
            echo "<td class='center'>";
606
            Html::showSimpleForm(static::getFormURL(),
moyooo's avatar
moyooo committed
607
                                 'test_ldap_replicate', _sx('button', 'Test'),
608 609
                                 ['id'                => $ID,
                                       'ldap_replicate_id' => $ldap_replicate["id"]]);
yllen's avatar
CS  
yllen committed
610
            echo "</td></tr>";
moyooo's avatar
moyooo committed
611
         }
moyooo's avatar
moyooo committed
612
         echo $header_begin.$header_bottom.$header_end;
moyooo's avatar
moyooo committed
613
         echo "</table>";
moyooo's avatar
moyooo committed
614
         $massiveactionparams['ontop'] = false;
moyooo's avatar
moyooo committed
615
         Html::showMassiveActions($massiveactionparams);
moyooo's avatar
moyooo committed
616

617
         Html::closeForm();
moyooo's avatar
end w3c  
moyooo committed
618
         echo "</div>";
moyooo's avatar
moyooo committed
619 620 621
      }
   }

moyooo's avatar
moyooo committed
622
   /**
Anael Mobilia's avatar
PHPdoc  
Anael Mobilia committed
623 624
    * Build a dropdown
    *
Anael Mobilia's avatar
Anael Mobilia committed
625
    * @since 0.84
yllen's avatar
CS  
yllen committed
626
    *
Anael Mobilia's avatar
PHPdoc  
Anael Mobilia committed
627 628 629 630
    * @param array $options Options
    *
    * @return string
    */
moyooo's avatar
moyooo committed
631
   static function dropdownGroupSearchType(array $options) {
yllen's avatar
CS  
yllen committed
632

633 634 635 636 637
      $p = [
         'name'    => 'group_search_type',
         'value'   => self::GROUP_SEARCH_USER,
         'display' => true,
      ];
moyooo's avatar
moyooo committed
638 639 640 641 642 643 644 645 646 647 648 649 650 651

      if (count($options)) {
         foreach ($options as $key => $val) {
            $p[$key] = $val;
         }
      }

      $tab = self::getGroupSearchTypeName();
      return Dropdown::showFromArray($p['name'], $tab, $p);
   }

   /**
    * Get the possible value for contract alert
    *
Anael Mobilia's avatar
Anael Mobilia committed
652
    * @since 0.83
moyooo's avatar
moyooo committed
653
    *
Anael Mobilia's avatar
PHPdoc  
Anael Mobilia committed
654
    * @param integer $val if not set, ask for all values, else for 1 value (default NULL)
moyooo's avatar
moyooo committed
655
    *
Anael Mobilia's avatar
PHPdoc  
Anael Mobilia committed
656 657
    * @return array|string
    */
658
   static function getGroupSearchTypeName($val = null) {
Johan Cwiklinski's avatar
Johan Cwiklinski committed
659 660 661 662 663
      $tmp = [
         self::GROUP_SEARCH_USER    => __('In users'),
         self::GROUP_SEARCH_GROUP   => __('In groups'),
         self::GROUP_SEARCH_BOTH    => __('In users and groups')
      ];
moyooo's avatar
moyooo committed
664 665 666 667 668 669 670 671 672

      if (is_null($val)) {
         return $tmp;
      }
      if (isset($tmp[$val])) {
         return $tmp[$val];
      }
      return NOT_AVAILABLE;
   }
yllen's avatar
CS  
yllen committed
673

Anael Mobilia's avatar
PHPdoc  
Anael Mobilia committed
674 675 676 677 678
   /**
    * Show group config form
    *
    * @return void
    */
yllen's avatar
yllen committed
679
   function showFormGroupsConfig() {
moyooo's avatar
moyooo committed
680

yllen's avatar
yllen committed
681 682
      $ID = $this->getField('id');

moyooo's avatar
end w3c  
moyooo committed
683
      echo "<div class='center'>";
yllen's avatar
yllen committed
684
      echo "<form method='post' action='".Toolbox::getItemTypeFormURL(__CLASS__)."'>";
moyooo's avatar
moyooo committed
685
      echo "<input type='hidden' name='id' value='$ID'>";
moyooo's avatar
end w3c  
moyooo committed
686
      echo "<table class='tab_cadre_fixe'>";
moyooo's avatar
moyooo committed
687

moyooo's avatar
end w3c  
moyooo committed
688
      echo "<tr><th class='center' colspan='4'>" . __('Belonging to groups') . "</th></tr>";
moyooo's avatar
moyooo committed
689

moyooo's avatar
moyooo committed
690
      echo "<tr class='tab_bg_1'><td>" . __('Search type') . "</td><td>";
691
      self::dropdownGroupSearchType(['value' => $this->fields["group_search_type"]]);
moyooo's avatar
moyooo committed
692
      echo "</td>";
moyooo's avatar
moyooo committed
693
      echo "<td>" . __('User attribute containing its groups') . "</td>";
moyooo's avatar
moyooo committed
694 695 696
      echo "<td><input type='text' name='group_field' value='".$this->fields["group_field"]."'>";
      echo "</td></tr>";

yllen's avatar
CS  
yllen committed
697
      echo "<tr class='tab_bg_1'><td>" . __('Filter to search in groups')."</td><td colspan='3'>";
698 699
      echo "<textarea cols='100' rows='1' name='group_condition'>".$this->fields["group_condition"];
      echo "</textarea>";
moyooo's avatar
moyooo committed
700 701
      echo "</td></tr>";

moyooo's avatar
moyooo committed
702
      echo "<tr class='tab_bg_1'><td>" . __('Group attribute containing its users') . "</td>";
moyooo's avatar
moyooo committed
703 704
      echo "<td><input type='text' name='group_member_field' value='".
                 $this->fields["group_member_field"]."'></td>";
moyooo's avatar
moyooo committed
705
      echo "<td>" . __('Use DN in the search') . "</td>";
moyooo's avatar
moyooo committed
706
      echo "<td>";
yllen's avatar
yllen committed
707
      Dropdown::showYesNo("use_dn", $this->fields["use_dn"]);
moyooo's avatar
moyooo committed
708
      echo "</td></tr>";
yllen's avatar
yllen committed
709

yllen's avatar
fix W3C  
yllen committed
710
      echo "<tr class='tab_bg_2'><td class='center' colspan='4'>";
711
      echo "<input type='submit' name='update' class='submit' value=\"".__s('Save')."\">";
moyooo's avatar
moyooo committed
712
      echo "</td></tr>";
713 714 715
      echo "</table>";
      Html::closeForm();
      echo "</div>";
moyooo's avatar
moyooo committed
716 717
   }

Anael Mobilia's avatar
PHPdoc  
Anael Mobilia committed
718 719 720 721 722
   /**
    * Show ldap test form
    *
    * @return void
    */
yllen's avatar
yllen committed
723
   function showFormTestLDAP () {
moyooo's avatar
moyooo committed
724

yllen's avatar
yllen committed
725 726
      $ID = $this->getField('id');

yllen's avatar
yllen committed
727
      if ($ID > 0) {
moyooo's avatar
end w3c  
moyooo committed
728
         echo "<div class='center'>";
yllen's avatar
yllen committed
729
         echo "<form method='post' action='".Toolbox::getItemTypeFormURL(__CLASS__)."'>";
yllen's avatar
CS  
yllen committed
730
         echo "<input type='hidden' name='id' value='$ID'>";
moyooo's avatar
end w3c  
moyooo committed
731
         echo "<table class='tab_cadre_fixe'>";
moyooo's avatar
moyooo committed
732
         echo "<tr><th colspan='4'>" . __('Test of connection to LDAP directory') . "</th></tr>";
yllen's avatar
yllen committed
733

moyooo's avatar
moyooo committed
734
         if (isset($_SESSION["LDAP_TEST_MESSAGE"])) {
yllen's avatar
fix W3C  
yllen committed
735
            echo "<tr class='tab_bg_2'><td class='center' colspan='4'>";
moyooo's avatar
moyooo committed
736 737 738 739
            echo $_SESSION["LDAP_TEST_MESSAGE"];
            echo"</td></tr>";
            unset($_SESSION["LDAP_TEST_MESSAGE"]);
         }
yllen's avatar
yllen committed
740

yllen's avatar
fix W3C  
yllen committed
741
         echo "<tr class='tab_bg_2'><td class='center' colspan='4'>";
yllen's avatar
yllen committed
742
         echo "<input type='submit' name='test_ldap' class='submit' value=\"".
743
                _sx('button', 'Test')."\">";
yllen's avatar
yllen committed
744
         echo "</td></tr>";
moyooo's avatar
end w3c  
moyooo committed
745
         echo "</table>";
746
         Html::closeForm();
moyooo's avatar
end w3c  
moyooo committed
747
         echo "</div>";
moyooo's avatar
moyooo committed
748 749 750
      }
   }

Anael Mobilia's avatar
PHPdoc  
Anael Mobilia committed
751 752 753 754 755
   /**
    * Show user config form
    *
    * @return void
    */
yllen's avatar
yllen committed
756
   function showFormUserConfig() {
moyooo's avatar
moyooo committed
757

yllen's avatar
yllen committed
758 759
      $ID = $this->getField('id');

moyooo's avatar
end w3c  
moyooo committed
760
      echo "<div class='center'>";
yllen's avatar
yllen committed
761
      echo "<form method='post' action='".Toolbox::getItemTypeFormURL(__CLASS__)."'>";
moyooo's avatar
moyooo committed
762
      echo "<input type='hidden' name='id' value='$ID'>";
moyooo's avatar
end w3c  
moyooo committed
763
      echo "<table class='tab_cadre_fixe'>";
moyooo's avatar
moyooo committed
764 765

      echo "<tr class='tab_bg_1'>";
moyooo's avatar
moyooo committed
766
      echo "<th class='center' colspan='4'>" . __('Binding to the LDAP directory') . "</th></tr>";
moyooo's avatar
moyooo committed
767

remi's avatar
gettext  
remi committed
768
      echo "<tr class='tab_bg_2'><td>" . __('Surname') . "</td>";
yllen's avatar
yllen committed
769 770
      echo "<td><input type='text' name='realname_field' value='".
                 $this->fields["realname_field"]."'></td>";
remi's avatar
gettext  
remi committed
771
      echo "<td>" . __('First name') . "</td>";
yllen's avatar
yllen committed
772 773
      echo "<td><input type='text' name='firstname_field' value='".
                 $this->fields["firstname_field"]."'></td></tr>";
moyooo's avatar
moyooo committed
774

remi's avatar
gettext  
remi committed
775
      echo "<tr class='tab_bg_2'><td>" . __('Comments') . "</td>";
yllen's avatar
yllen committed
776 777
      echo "<td><input type='text' name='comment_field' value='".$this->fields["comment_field"]."'>";
      echo "</td>";
moyooo's avatar
gettext  
moyooo committed
778
      echo "<td>" . __('Administrative number') . "</td>";
779 780 781 782 783 784
      echo "<td>";
      echo "<input type='text' name='registration_number_field' value='".
             $this->fields["registration_number_field"]."'>";
      echo "</td></tr>";

      echo "<tr class='tab_bg_2'>";
moyooo's avatar
moyooo committed
785
      echo "<td>" . __('Email') . "</td>";
moyooo's avatar
moyooo committed
786
      echo "<td><input type='text' name='email1_field' value='".$this->fields["email1_field"]."'>";
787
      echo "</td>";
788
      echo "<td>" . sprintf(__('%1$s %2$s'), _n('Email', 'Emails', 1), '2') . "</td>";
789 790 791 792
      echo "<td><input type='text' name='email2_field' value='".$this->fields["email2_field"]."'>";
      echo "</td></tr>";

      echo "<tr class='tab_bg_2'>";
793
      echo "<td>" . sprintf(__('%1$s %2$s'), _n('Email', 'Emails', 1), '3') . "</td>";
794 795
      echo "<td><input type='text' name='email3_field' value='".$this->fields["email3_field"]."'>";
      echo "</td>";
796
      echo "<td>" . sprintf(__('%1$s %2$s'), _n('Email', 'Emails', 1), '4') . "</td>";
797
      echo "<td><input type='text' name='email4_field' value='".$this->fields["email4_field"]."'>";
moyooo's avatar
moyooo committed
798 799
      echo "</td></tr>";

800
      echo "<tr class='tab_bg_2'><td>" . __('Phone') . "</td>";
moyooo's avatar
moyooo committed
801 802
      echo "<td><input type='text' name='phone_field'value='".$this->fields["phone_field"]."'>";
      echo "</td>";
803
      echo "<td>" .  __('Phone 2') . "</td>";
moyooo's avatar
moyooo committed
804 805 806
      echo "<td><input type='text' name='phone2_field'value='".$this->fields["phone2_field"]."'>";
      echo "</td></tr>";

moyooo's avatar
moyooo committed
807
      echo "<tr class='tab_bg_2'><td>" . __('Mobile phone') . "</td>";
moyooo's avatar
moyooo committed
808 809
      echo "<td><input type='text' name='mobile_field'value='".$this->fields["mobile_field"]."'>";
      echo "</td>";
810
      echo "<td>" . _x('person', 'Title') . "</td>";
moyooo's avatar
moyooo committed
811 812 813
      echo "<td><input type='text' name='title_field' value='".$this->fields["title_field"]."'>";
      echo "</td></tr>";

moyooo's avatar
gettext  
moyooo committed
814
      echo "<tr class='tab_bg_2'><td>" . __('Category') . "</td>";
yllen's avatar
yllen committed
815 816
      echo "<td><input type='text' name='category_field' value='".
                 $this->fields["category_field"]."'></td>";
817
      echo "<td>" . __('Language') . "</td>";
yllen's avatar
yllen committed
818 819
      echo "<td><input type='text' name='language_field' value='".
                 $this->fields["language_field"]."'></td></tr>";
yllen's avatar
yllen committed
820