1 <?php
3 class termservice extends plugin
4 {
5 /* Generic terminal attributes */
6 var $gotoLpdEnable= FALSE;
7 var $gotoXMonitor= "";
8 var $gotoXMethod= "default";
9 var $gotoXdmcpServer= "";
10 var $gotoXDriver= "";
11 var $gotoXResolution= "";
12 var $gotoXColordepth= "";
13 var $gotoXHsync= "";
14 var $gotoXVsync= "";
15 var $gotoXKbModel= "";
16 var $gotoXKbLayout= "";
17 var $gotoXKbVariant= "";
18 var $gotoXMouseType= "";
19 var $gotoXMouseport= "";
20 var $gotoLpdServer= "";
21 var $gotoScannerEnable= "";
22 var $gotoScannerModel= "";
23 var $gotoScannerClients= "";
24 var $gotoScannerBackend= "";
25 var $goFonHardware= "automatic";
27 var $AutoSync = false;
28 var $view_logged = FALSE;
30 /* Needed values and lists */
31 var $ignore_account= TRUE;
32 var $base= "";
33 var $cn= "";
34 var $orig_dn= "";
35 var $XMethods= array();
36 var $XDrivers= array();
37 var $XResolutions= array();
38 var $XColordepths= array();
39 var $XKbModels= array ();
40 var $XKbLayouts= array ();
41 var $XKbVariants= array ();
42 var $MouseTypes= array();
43 var $MousePorts= array();
44 var $hardware_list= array();
45 var $used_hardware= array();
48 /* attribute list for save action */
49 var $attributes= array("gotoLpdEnable", "gotoXMonitor", "gotoXMethod", "gotoXdmcpServer",
50 "gotoXDriver", "gotoXResolution", "gotoXColordepth",
51 "gotoXHsync", "gotoXVsync", "gotoLpdEnable", "gotoLpdServer",
52 "gotoScannerEnable", "gotoScannerModel", "gotoScannerClients",
53 "gotoScannerBackend", "gotoXKbModel", "gotoXKbLayout", "gotoXKbVariant",
54 "gotoXMouseType", "gotoXMouseport", "goFonHardware");
55 var $objectclasses= array("GOhard");
57 var $is_ogroup = FALSE;
58 var $is_ogroup_member = FALSE;
60 var $selected_xdmcp_servers = array();
61 var $inherited_xdmcp_servers = array();
63 function termservice (&$config, $dn= NULL, $parent= NULL)
64 {
65 plugin::plugin ($config, $dn, $parent);
66 $this->orig_dn= $this->dn;
68 /* Check if we are a part of an ogroup.
69 * In this case, we have to hide all the inherit stuff.
70 */
71 if(isset($parent) && get_class($parent) == "ogroup"){
72 $this->is_ogroup = TRUE;
73 }
75 /* Check if we are member of an object group.
76 */
77 $ldap= $this->config->get_ldap_link();
78 $ldap->cd($this->config->current['BASE']);
79 $ldap->search("(&(objectClass=gotoWorkstationTemplate)(member=".LDAP::prepare4filter($this->dn)."))");
80 if($ldap->count()){
81 $this->is_ogroup_member = TRUE;
82 }
84 /* Get list of available xdrivers */
85 $this->XDrivers = $this->getListOfXDrivers();
87 /* Create a list of available resolutions.
88 */
89 $this->XResolutions= array(
90 "640x480" => "640x480",
91 "800x600" => "800x600",
92 "1024x768" => "1024x768",
93 "1152x864" => "1152x864",
94 "1280x1024" => "1280x1024",
95 "1400x1050" => "1400x1050",
96 "1600x1200" => "1600x1200");
98 if(isset($this->config->data['MAIN']['RESOLUTION_HOOK'])){
99 $file = $this->config->data['MAIN']['RESOLUTION_HOOK'];
100 if(is_readable($file)){
101 $str = file_get_contents($file);
102 $lines = split("\n",$str);
103 foreach($lines as $line){
104 $line = trim($line);
105 if(!empty($line)){
106 $this->XResolutions[$line]=$line;
107 }
108 }
109 }else{
110 print_red(sprintf(_("You have specified an external resolution hook which can't be read, please check the permission of the file '%s'."),$file));
111 }
112 }
114 /* Create a set of selectable color depths
115 */
116 $this->XColordepths= array(
117 "8" => "8 " ._("bit"),
118 "15" => "15 "._("bit"),
119 "16" => "16 "._("bit"),
120 "24" => "24 "._("bit"));
123 /* Create a set of selectable keyboard models
124 */
125 $this->XKbModels = array();
126 foreach(array ("btc9000", "chicony", "compaq", "dell", "dell101", "everex",
127 "flexpro", "geniuscomfy", "hp", "itouch", "jp106", "logicordless",
128 "logiinetnav", "logiinternet", "macintosh", "microsoft",
129 "microsoftpro", "omnikey101", "pc101", "pc102", "pc104",
130 "pc105", "rapidaccess", "rapidaccess2", "winbook") as $type){
131 $this->XKbModels[$type] = $type;
132 }
134 /* Additional values will be extracted from CONFIG_DIR.keyboardLayouts */
135 $this->XKbLayouts = array("de"=> "de","intl" =>"intl","us" =>"us");
136 $this->XKbVariants= array("nodeadkeys"=>"nodeadkeys", "basic"=>"basic");
137 $this->MouseTypes = array("ImPS/2"=>"ImPS/2","PS/2"=>"PS/2", "explorerps/2"=>"explorerps/2",
138 "Microsoft"=>"Microsoft","Logitech"=>"Logitech");
139 $this->MousePorts = array("/dev/ttyS0"=>"/dev/ttyS0",
140 "/dev/ttyS1"=>"/dev/ttyS1","/dev/psaux"=>"/dev/psaux",
141 "/dev/input/mice"=>"/dev/input/mice");
144 /* Try to read additional keyboard layouts
145 */
146 if(file_exists(CONFIG_DIR."/keyboardLayouts")){
147 if(is_readable(CONFIG_DIR."/keyboardLayouts")){
148 $str = file_get_contents(CONFIG_DIR."/keyboardLayouts");
149 $tmp = split("\n",$str);
150 foreach($tmp as $entry){
151 if((!empty($entry)) && (!preg_match("/^#/",$entry))){
152 $entry = trim($entry);
153 $tmp2 = split ("\:",$entry);
154 $la = trim($tmp2[0]); // What would be saved to ldap
155 $da = trim($tmp2[1]); // This wis displayed in the listbox
156 $this->XKbLayouts [ $la] = $da;
157 }
158 }
159 }
160 }
162 /* Terminal server methods
163 */
164 if($this->is_ogroup_member){
165 $this->XMethods["default"]= _("inherited");
166 }
167 $this->XMethods["xdmcp"] = _("XDMCP");
168 $this->XMethods["ldm"] = _("LDM");
169 $this->XMethods["rdp"] = _("Windows RDP");
170 $this->XMethods["citrix"] = _("ICA client");
172 /* Get selected gotoXdmcpServer
173 */
174 $this->selected_xdmcp_servers = array();
175 if(isset($this->attrs['gotoXdmcpServer'])){
176 for($i = 0 ; $i < $this->attrs['gotoXdmcpServer']['count'] ; $i++){
177 $this->selected_xdmcp_servers[] = $this->attrs['gotoXdmcpServer'][$i];
178 }
179 }
181 /* Load phone hardware list
182 */
183 $tmp = get_sub_list("(objectClass=goFonHardware)","",array(get_ou("phoneou")),
184 $this->config->current['BASE'],array("cn","description"), GL_NO_ACL_CHECK);
185 foreach($tmp as $attrs){
186 $cn= $attrs['cn'][0];
187 $description= "";
188 if (isset($attrs['description'])){
189 $description= " - ".$attrs['description'][0];
190 }
191 $this->hardware_list[$cn]= "$cn$description";
192 }
193 $this->hardware_list["automatic"]= _("automatic");
194 ksort($this->hardware_list);
196 /* These departments may contain objects that have
197 goFonHardware set.
198 */
199 $deps_a = array(
200 get_people_ou(),
201 get_ou("ogroupou"),
202 get_ou("serverou"),
203 get_ou("terminalou"),
204 get_ou("workstationou"),
205 get_ou("printerou"),
206 get_ou("componentou"),
207 get_ou("phoneou"));
209 $tmp = get_sub_list("(goFonHardware=*)","",$deps_a,$this->config->current['BASE'],
210 array('cn','dn','goFonHardware'),GL_NO_ACL_CHECK);
211 foreach($tmp as $attrs){
212 $cn = $attrs['goFonHardware'][0];
213 if(isset($this->hardware_list[$cn])){
214 $this->used_hardware[$cn]= $cn;
215 }
216 }
218 /* Convert gotoLpdEnable
219 */
220 $this->gotoLpdEnable= preg_match("/yes/i",$this->gotoLpdEnable);
222 /* Load hardware list */
223 $ldap= $this->config->get_ldap_link();
224 $ldap->cd($this->config->current['BASE']);
225 $map= array(
226 "gotoXResolution",
227 "gotoXColordepth",
228 "gotoXKbModel",
229 "gotoXKbLayout",
230 # "gotoXDriver",
231 "gotoXdmcpServer",
232 "gotoXKbVariant",
233 "gotoXMouseType",
234 "gotoXMethod",
235 "gotoXMouseport");
236 $ldap->search("(&(objectClass=gotoWorkstationTemplate)(member=".LDAP::prepare4filter($this->dn)."))",$map);
237 if ($ldap->count() == 1){
238 $attrs= $ldap->fetch();
239 foreach ($map as $name){
240 if (!isset($attrs[$name][0])){
241 continue;
242 }
244 switch ($name){
245 # case 'gotoXDriver':
246 # $this->XDrivers = array('default' => _("inherited").' ['.$attrs[$name][0].']' ) + $this->XDrivers;
247 # break;
248 case 'gotoXMethod':
249 $this->XMethods = array('default' => _("inherited").' ['.$attrs[$name][0].']' ) + $this->XMethods;
250 if(isset($attrs['gotoXdmcpServer'])){
251 for($i = 0 ; $i < $attrs['gotoXdmcpServer']['count'] ; $i++){
252 $this->inherited_xdmcp_servers[] = $attrs['gotoXdmcpServer'][$i];
253 }
254 }
255 break;
256 case 'gotoXResolution':
257 $this->XResolutions= array('default' => _("inherited").' ['.$attrs[$name][0].']' ) + $this->XResolutions;
258 break;
259 case 'gotoXColordepth':
260 $this->XColordepths= array('default' => _("inherited").' ['.$attrs[$name][0].' '._('Bit').']') + $this->XColordepths;
261 break;
262 case 'gotoXKbModel':
263 $this->XKbModels= array('default' => _("inherited").' ['.$attrs[$name][0].']') + $this->XKbModels;
264 break;
265 case 'gotoXKbLayout':
266 $this->XKbLayouts= array('default' => _("inherited").' ['.$attrs[$name][0].']' ) + $this->XKbLayouts;
267 break;
268 case 'gotoXKbVariant':
269 $this->XKbVariants= array('default' => _("inherited").' ['.$attrs[$name][0].']' ) + $this->XKbVariants;
270 break;
271 case 'gotoXMouseType':
272 $this->MouseTypes= array('AUTO' => _("inherited").' ['.$attrs[$name][0].']' ) + $this->MouseTypes;
273 break;
274 case 'gotoXMouseport':
275 $this->MousePorts= array('AUTO' => _("inherited").' ['.$attrs[$name][0].']' ) + $this->MousePorts;
276 break;
277 }
278 }
279 }
281 if(preg_match("/\+/",$this->gotoXHsync)){
282 $this->AutoSync = true;
283 $this->gotoXHsync = preg_replace("/\+/","-",$this->gotoXHsync);
284 $this->gotoXVsync = preg_replace("/\+/","-",$this->gotoXVsync);
285 }
286 }
289 function execute()
290 {
291 /* Call parent execute */
292 plugin::execute();
294 if($this->is_account && !$this->view_logged){
295 $this->view_logged = TRUE;
296 new log("view","terminal/".get_class($this),$this->dn);
297 }
299 /* Do we need to flip is_account state? */
300 if (isset($_POST['modify_state'])){
301 $this->is_account= !$this->is_account;
302 }
304 /* Do we represent a valid terminal? */
305 if (!$this->is_account && $this->parent === NULL){
306 $display= "<img alt=\"\" src=\"images/stop.png\" align=\"middle\"> <b>".
307 _("This 'dn' has no terminal features.")."</b>";
308 return ($display);
309 }
311 /* Show main page */
312 $smarty= get_smarty();
314 /* Assign acls */
315 $tmp= $this->plInfo();
316 foreach($tmp['plProvidedAcls'] as $name => $translation){
317 $smarty->assign($name."ACL",$this->getacl($name));
318 }
320 if(isset($_POST['gotoXdmcpServer_add']) && isset($_POST['XdmcpAddServer'])){
321 if(!in_array($_POST['gotoXdmcpServer_add'],$this->selected_xdmcp_servers)){
322 $this->selected_xdmcp_servers[] = $_POST['gotoXdmcpServer_add'];
323 }
324 }
326 if(isset($_POST['selected_xdmcp_servers']) && isset($_POST['XdmcpDelServer'])){
327 $tmp = array();
328 foreach($this->selected_xdmcp_servers as $name){
329 if(!in_array($name,$_POST['selected_xdmcp_servers'])){
330 $tmp[] = $name;
331 }
332 }
333 $this->selected_xdmcp_servers = $tmp;
334 }
336 /* Arrays */
337 foreach(array("XMethods", "XDrivers", "XResolutions", "XColordepths",
338 "XKbModels","XKbVariants","MouseTypes", "MousePorts") as $val){
339 $smarty->assign("$val", $this->$val);
340 }
341 $smarty->assign("XKbLayouts", $this->XKbLayouts);
342 $smarty->assign("XKbLayoutKeys",array_flip($this->XKbLayouts));
343 $smarty->assign("nfsservers", $this->config->data['SERVERS']['NFS']);
346 /* Create a list of useable servers for the currently selected
347 * connection type.
348 */
349 $xdmcp_types = $this->config->data['SERVERS']['TERMINAL_SESSION_TYPES'];
350 $available_servers = array();
351 foreach($xdmcp_types as $servername =>$supported_types){
352 if(in_array(strtoupper($this->gotoXMethod),$supported_types)){
353 $available_servers[] = $servername;
354 }
355 }
357 /* Append additional information to invalid server selections.
358 */
359 $tmp = array();
360 foreach($this->selected_xdmcp_servers as $server){
361 if(in_array($server,$available_servers)){
362 $tmp[$server] = $server;
363 }else{
364 $tmp[$server] = $server." - "._("Unsupported");
365 }
366 }
368 /* Remove already selected servers from available list.
369 */
370 foreach($available_servers as $key => $server){
371 if(isset($tmp[$server])){
372 unset($available_servers[$key]);
373 }
374 }
376 $smarty->assign("selected_xdmcp_servers", $tmp);
377 $smarty->assign("inherited_xdmcp_servers", $this->inherited_xdmcp_servers);
378 $smarty->assign("available_xdmcp_servers", $available_servers);
380 /* Variables - select */
381 foreach(array("gotoXMethod","gotoXDriver", "gotoXResolution", "gotoXColordepth",
382 "gotoLpdServer", "gotoXKbModel", "gotoXKbLayout",
383 "gotoXKbVariant", "gotoXMouseType", "gotoXMouseport") as $val){
385 $smarty->assign($val."_select", $this->$val);
386 }
388 /* Variables */
389 foreach(array("gotoXHsync", "gotoXVsync") as $val){
390 $smarty->assign($val, $this->$val);
391 }
392 $smarty->assign("staticAddress", "");
394 /* Checkboxes */
395 foreach(array("gotoLpdEnable", "gotoScannerEnable") as $val){
396 if ($this->$val == TRUE) {
397 $smarty->assign("$val", "checked");
398 } else {
399 $smarty->assign("$val", "");
400 }
401 }
403 /* Phone stuff */
404 $smarty->assign ("goFonHardware", $this->goFonHardware);
405 $hl= "<select size=\"1\" name=\"goFonHardware\" title=\"".
406 _("Choose the phone located at the current terminal")."\" >\n";
407 foreach ($this->hardware_list as $cn => $description){
408 if ($cn == $this->goFonHardware){
409 $selected= "selected";
410 } else {
411 $selected= "";
412 }
413 if (isset($this->used_hardware[$cn])){
414 $color= "style=\"color:#A0A0A0\"";
415 } else {
416 $color= "";
417 }
418 $hl.= " <option $color label=\"$cn\" value=\"$cn\" $selected>$description</option>\n";
419 }
420 $hl.= "</select>\n";
421 $smarty->assign ("hardware_list", $hl);
422 $smarty->assign ("gotoXMonitor", $this->gotoXMonitor);
424 $smarty->assign("AutoSyncACL",$this->getacl("AutoSync"));
426 $smarty->assign("AutoSyncCHK"," ");
427 if($this->AutoSync){
428 $smarty->assign("AutoSyncCHK"," checked ");
429 $smarty->assign("gotoXVsyncACL", preg_replace("/w/","",$this->getacl("gotoXVsync")));
430 $smarty->assign("gotoXHsyncACL", preg_replace("/w/","",$this->getacl("gotoXHsync")));
431 }
433 /* Show main page */
434 return($smarty->fetch (get_template_path('terminalService.tpl', TRUE,dirname(__FILE__))));
435 }
437 function remove_from_parent()
438 {
439 new log("remove","terminal/".get_class($this),$this->dn,array_keys($this->attrs));
440 $this->handle_post_events("remove");
442 plugin::remove_from_parent();
443 $ldap = $this->config->get_ldap_link();
444 $ldap->cd($this->dn);
445 $ldap->modify($this->attrs);
446 if (!$ldap->success()){
447 msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $this->dn, LDAP_MOD, get_class()));
448 }
449 }
452 /* Save data to object */
453 function save_object()
454 {
455 plugin::save_object();
457 /* Save checkbox state */
458 if (isset ($_POST['gotoXMethod'])){
459 foreach (array("gotoLpdEnable", "gotoScannerEnable") as $val){
461 if($this->acl_is_writeable($val)){
462 if (!isset ($_POST["$val"])){
463 $this->$val= FALSE;
464 } else {
465 $this->$val= TRUE;
466 }
467 }
468 }
469 }
471 if(isset($_POST['gotoXDriver'])){
472 if(isset($_POST['AutoSync'])){
473 $this->AutoSync = true;
474 }else{
475 $this->AutoSync = false;
476 }
477 }
479 /* Default entries can use blank hsync/vsync entries */
480 if ($this->dn != "" && $this->cn != "default" && $this->cn != "default"){
482 /* But only if no auto sync is enabled... */
483 if (!$this->AutoSync){
485 /* Check vsync for correct usage */
486 $val= preg_replace ("/\s/", "", $this->gotoXVsync);
487 if (!preg_match ("/^\d+(\.\d+)?([-]\d+(\.\d+)?)?$/", $val) && $this->acl_is_writeable("gotoXVsync")){
489 $message[]= _("Please specify a valid VSync range.");
490 } elseif ($this->acl_is_writeable("gotoXVsync")){
491 list($v1,$v2)= preg_split ("/[-+]/", $val);
492 if ($v2 != ""){
493 if ($v1 > $v2){
494 $message[]= _("Please specify a valid VSync range.");
495 }
496 }
497 }
499 /* Check hsync for correct usage */
500 $val= preg_replace ("/\s/", "", $this->gotoXHsync);
501 if (!preg_match ("/^\d+(\.\d+)?([-]\d+(\.\d+)?)?$/", $val) && $this->acl_is_writeable("gotoXHsync")){
503 $message[]= _("Please specify a valid HSync range.");
504 } elseif ($this->acl_is_writeable("gotoXHsync")){
505 list($v1,$v2)= preg_split ("/[-+]/", $val);
506 if ($v2 != ""){
507 if ($v1 > $v2){
508 $message[]= _("Please specify a valid HSync range.");
509 }
510 }
511 }
512 }
513 }
514 }
517 /* Check supplied data */
518 function check()
519 {
520 /* Call common method to give check the hook */
521 $message= plugin::check();
523 if($this->gotoXMethod != "default"){
524 $xdmcp_types = $this->config->data['SERVERS']['TERMINAL_SESSION_TYPES'];
525 $available_servers = array();
526 foreach($xdmcp_types as $servername =>$supported_types){
527 if(in_array(strtoupper($this->gotoXMethod),$supported_types)){
528 $available_servers[] = $servername;
529 }
530 }
531 foreach($this->selected_xdmcp_servers as $server){
532 if(!in_array($server,$available_servers)){
533 $message[] = _("Remote desktop settings contains servers that do not support the selected connection method.");
534 break;
535 }
536 }
537 }
539 return ($message);
540 }
543 /* Save to LDAP */
544 function save()
545 {
546 /* Convert to string */
547 $this->gotoLpdEnable= $this->gotoLpdEnable?"Yes":"No";
549 if($this->is_ogroup){
550 $this->objectclasses = array("gotoWorkstationTemplate");
551 }else{
552 $this->objectclasses = array("GOhard");
553 }
555 plugin::save();
557 /* Strip out 'default' values */
558 foreach(array("gotoXMethod","gotoXDriver", "gotoXResolution", "gotoXColordepth",
559 "gotoLpdServer", "gotoXKbModel", "gotoXKbLayout",
560 "gotoXKbVariant", "gotoXMouseType", "gotoXMouseport") as $val){
562 if ($this->attrs[$val] == "default"){
563 $this->attrs[$val]= array();
564 }
565 }
567 if($this->gotoXMethod == "default"){
568 $this->attrs['gotoXdmcpServer'] = array();
569 $this->attrs['gotoXMethod'] = array();
570 }else{
571 $this->attrs['gotoXdmcpServer'] = array_values($this->selected_xdmcp_servers);
572 }
575 if($this->AutoSync){
576 $this->attrs['gotoXHsync'] = "30+55";
577 $this->attrs['gotoXVsync'] = "50+70";
578 }
580 /* Write back to ldap */
581 $ldap= $this->config->get_ldap_link();
582 $ldap->cd($this->dn);
583 $this->cleanup();
584 $ldap->modify ($this->attrs);
585 new log("modify","terminal/".get_class($this),$this->dn,array_keys($this->attrs),$ldap->get_error());
587 if (!$ldap->success()){
588 msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $this->dn, LDAP_MOD, get_class()));
589 }
590 $this->handle_post_events("modify");
591 }
594 function getListOfXDrivers()
595 {
596 $drivers = array("default" => "["._("unknown")."]");
598 /* Generate a list of xdrivers from CONFIG_DIR/xdrivers */
599 if (file_exists(CONFIG_DIR.'/xdrivers')){
600 $xdrivers = file (CONFIG_DIR.'/xdrivers');
601 foreach ($xdrivers as $line){
602 if (!preg_match ("/^#/", $line)){
603 $drivers[]= trim($line);
604 }
605 }
606 } else {
607 foreach( array(
608 "ati", "atimisc", "chips", "cirrus", "cyrix", "fbdev", "fglrx",
609 "i128", "i740", "i810", "imstt", "mga", "neomagic", "newport", "nsc", "nv", "nvidia",
610 "r128", "radeon", "rendition", "s3", "s3virge", "savage", "siliconmotion",
611 "sis", "tdfx", "tga", "trident", "tseng", "vesa", "vga", "vmware") as $driver){
612 $drivers[] = $driver;
613 }
614 }
615 return($drivers);
616 }
619 /* Return plugin informations for acl handling */
620 static function plInfo()
621 {
622 return (array(
623 "plShortName" => _("Service"),
624 "plDescription" => _("Terminal service"),
625 "plSelfModify" => FALSE,
626 "plDepends" => array(),
627 "plPriority" => 3,
628 "plSection" => array("administration"),
629 "plCategory" => array("terminal"),
631 "plProvidedAcls"=> array(
632 "gotoXMonitor" => _("Monitor"),
633 "gotoXMethod" => _("Method"),
634 "gotoXdmcpServer" => _("Remote desktop"),
635 "gotoXDriver" => _("Gfx driver"),
636 "gotoXResolution" => _("Gfx resolution"),
637 "gotoXColordepth" => _("Gfx color depth"),
638 "gotoXHsync" => _("Hsync"),
639 "gotoXVsync" => _("Vsync"),
640 "AutoSync" => _("Auto-Sync"),
641 "gotoLpdEnable" => _("Printer service enabled"),
642 "gotoLpdServer" => _("Spool server"),
643 "gotoScannerEnable" => _("Scanner enabled"),
644 "gotoXKbModel" => _("Keyboard model"),
645 "gotoXKbLayout" => _("Keyboard layout"),
646 "gotoXKbVariant" => _("Keyboard variant"),
647 "gotoXMouseType" => _("Mouse type"),
648 "gotoXMouseport" => _("Mouse port"),
649 "goFonHardware" => _("Telephone hardware"))
650 ));
651 }
652 }
654 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
655 ?>