From 057bbd8106b65f39cdfb3ea9deacae1764b86cc0 Mon Sep 17 00:00:00 2001 From: hickert Date: Wed, 10 Jan 2007 09:18:51 +0000 Subject: [PATCH] Added multiserver funktions for conferences git-svn-id: https://oss.gonicus.de/repositories/gosa/trunk@5510 594d385d-05f5-0310-b6e9-bd551577e9d8 --- .../conference/class_divListConferences.inc | 2 +- .../class_phoneConferenceGeneric.inc | 387 ++++++++++++------ plugins/gofon/conference/generic.tpl | 11 +- .../gofon/phoneaccount/class_phoneAccount.inc | 8 +- 4 files changed, 274 insertions(+), 134 deletions(-) diff --git a/plugins/gofon/conference/class_divListConferences.inc b/plugins/gofon/conference/class_divListConferences.inc index c2cedad4e..15f66f1db 100755 --- a/plugins/gofon/conference/class_divListConferences.inc +++ b/plugins/gofon/conference/class_divListConferences.inc @@ -200,7 +200,7 @@ class divListConference extends MultiSelectWindow $a_field3 = array("string"=> $cn , "attach" => $title." style='width:200px;'"); $a_field4 = array("string"=> $pin, "attach" => $title." style='width:50px;'"); $a_field5 = array("string"=> preg_replace("/%KEY%/",$conferencekey,$actions), - "attach"=> $title."style='width:52px;border-right:0px;text-align:right;'"); + "attach"=> $title."style='width:".$action_col_size."px;border-right:0px;text-align:right;'"); $this->AddElement(array($a_field1,$a_field2,$a_field3,$a_field4,$a_field5)); } diff --git a/plugins/gofon/conference/class_phoneConferenceGeneric.inc b/plugins/gofon/conference/class_phoneConferenceGeneric.inc index adce09d14..057401432 100644 --- a/plugins/gofon/conference/class_phoneConferenceGeneric.inc +++ b/plugins/gofon/conference/class_phoneConferenceGeneric.inc @@ -37,17 +37,16 @@ class conference extends plugin var $goFonConferenceOption_c = ""; // Count User var $goFonConferenceOption_D = ""; // Conference Type, no PIN/PIN - var $goFonConferenceOwner = ""; - - var $error_shown = false; + var $goFonConferenceOwner = ""; + var $goFonHomeServer = "0"; // Home server of the conference + var $init_HomeServer = "0"; // Initial home server of the conference + var $goFonHomeServers = array(); // All available home servers var $goFonConferenceOptionFormat = ""; var $goFonConferenceOptionLifetime = ""; var $telephoneNumber = ""; var $old_tele_number = false; - var $generate_error = ""; - var $old_dn; /* Headpage attributes */ @@ -57,8 +56,8 @@ class conference extends plugin var $dialog ; /* attribute list for save action */ - var $attributes= array("cn","base", "description", "goFonPIN","goFonConferenceOption_P","goFonConferenceOption_r", - "goFonConferenceOption_M","goFonConferenceOption_s","goFonConferenceOption_i","goFonConferenceOption_c", + var $attributes= array("cn","base", "description", "goFonPIN","goFonConferenceOption_P","goFonConferenceOption_r","goFonHomeServer", + "goFonConferenceOption_M","goFonConferenceOption_s","goFonConferenceOption_i","goFonConferenceOption_c","goFonHomeServer", "goFonConferenceOption_D","goFonConferenceOptionFormat","goFonConferenceOptionLifetime","telephoneNumber","goFonConferenceOwner"); var $objectclasses= array("top", "goFonConference"); @@ -67,10 +66,42 @@ class conference extends plugin { plugin::plugin($config, $dn, $plugin); $this->is_account = TRUE; - $this->ui = get_userinfo(); - $this->dn = $dn; + $this->ui = get_userinfo(); $this->orig_dn = $dn; - $this->config = $config; + + + /* Check server configurations + * Load all server configuration in $this->goFonHomeServers if available + * and use first server as default if necessary. + */ + $a_SETUP= array(); + if(array_key_exists('config',$_SESSION) && + array_key_exists('SERVERS',$_SESSION['config']->data) && + array_key_exists('FON',$_SESSION['config']->data['SERVERS']) && + count($_SESSION['config']->data['SERVERS']['FON']) && + is_callable("mysql_connect") + ) { + + /* Set available server */ + $this->goFonHomeServers = $_SESSION['config']->data['SERVERS']['FON']; + + /* Set default server */ + if($this->dn == "new"){ + $this->goFonHomeServer = $this->goFonHomeServers[0]['DN']; + } + + /* Remember inital home server, to be able to remove old entries */ + $this->init_HomeServer = $this->goFonHomeServer; + + /* get config */ + if(!isset($this->goFonHomeServers[$this->goFonHomeServer])){ + print_red(sprintf(_("The specified home server '%s' is not available in GOsa server configuration. Saving this account will create a new entry on the server '%s'. Use cancel if you do not want to create a new entry while ignoring old accounts."),$this->goFonHomeServer, $this->goFonHomeServers[0]['DN'])); + + $this->goFonHomeServer = $this->goFonHomeServers[0]['DN']; + $this->init_HomeServer = $this->goFonHomeServers[0]['DN']; + } + $cur_cfg = $this->goFonHomeServers[$this->goFonHomeServer]; + } /* Set base */ if ($this->dn == "new"){ @@ -81,7 +112,10 @@ class conference extends plugin $this->base= dn2base($ui->dn); } } else { - $this->base= preg_replace ("/^[^,]+,ou=conferences,ou=asterisk,ou=configs,ou=systems,/", "", $this->dn); + + /* The base is something like this + "cn=Confis,ou=conferences,ou=asterisk,ou=configs,ou=systems," */ + $this->base= preg_replace ("/^[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,/", "", $this->dn); } $this->goFonConferenceOwner=$this->ui->dn; @@ -108,6 +142,7 @@ class conference extends plugin $this->old_cn = $this->cn; } + function execute() { /* Call parent execute */ @@ -115,20 +150,8 @@ class conference extends plugin $smarty= get_smarty(); - - $tmp = $this->plInfo(); - foreach($tmp['plProvidedAcls'] as $name => $translation){ - $smarty->assign($name."ACL",$this->getacl($name)); - } - - if($this->acl_is_writeable("base")){ - $smarty->assign("baseSelect",true); - }else{ - $smarty->assign("baseSelect",false); - } - - $smarty->assign("bases" ,$this->get_allowed_bases()); - $smarty->assign("base_select" ,$this->base); + $smarty->assign("bases" ,$this->config->idepartments); + $smarty->assign("base" ,$this->base); $once = true; foreach($_POST as $name => $value){ @@ -147,23 +170,16 @@ class conference extends plugin if($this->dialog->isClosed()){ $this->dialog = false; }elseif($this->dialog->isSelected()){ - - /* A new base was selected, check if it is a valid one */ $tmp = $this->get_allowed_bases(); if(isset($tmp[$this->dialog->isSelected()])){ $this->base = $this->dialog->isSelected(); } - $this->dialog= false; }else{ return($this->dialog->execute()); } } - $smarty->assign("goFonConferenceOptions", array("D"=>"Conference ","d"=>"Conference without PIN")); - $smarty->assign("goFonConferenceOptionFormats", array("WAV"=>"Wave","GSM"=>"GSM","WAV49"=>"Wave49")); - $smarty->assign("goFonConferenceOption", $this->goFonConferenceOption_D); - foreach ($this->attributes as $val){ $smarty->assign("$val", $this->$val); if(!$this->$val){ @@ -173,6 +189,33 @@ class conference extends plugin } } + /* Create array with goFonHomeServer */ + $tmp = array(); + foreach($this->goFonHomeServers as $dn => $val){ + if(!is_numeric($dn)){ + $tmp[$dn] = $val['SERVER']; + } + } + $smarty->assign("goFonHomeServers",$tmp); + $smarty->assign("goFonConferenceOptions", array("D"=>"Conference ","d"=>"Conference without PIN")); + $smarty->assign("goFonConferenceOptionFormats", array("WAV"=>"Wave","GSM"=>"GSM","WAV49"=>"Wave49")); + $smarty->assign("goFonConferenceOption", $this->goFonConferenceOption_D); + + $tmp = $this->plInfo(); + foreach($tmp['plProvidedAcls'] as $name => $translation){ + $smarty->assign($name."ACL",$this->getacl($name)); + } + + if($this->acl_is_writeable("base")){ + $smarty->assign("baseSelect",true); + }else{ + $smarty->assign("baseSelect",false); + } + + $smarty->assign("bases" ,$this->get_allowed_bases()); + $smarty->assign("base_select" ,$this->base); + + if($_SESSION['js']==1){ if($this->goFonConferenceOption_P != "P"){ $smarty->assign("goFonPINACL", $this->getacl("goFonPIN",TRUE)); @@ -185,15 +228,28 @@ class conference extends plugin return($smarty->fetch (get_template_path('generic.tpl', TRUE))); } + function remove_from_parent() { - $this->SQL_remove_me(true); + /* Check if 'old' home server is available in gosa FON server configuration + * Try to remove this entry from database and display errors. + */ + if(isset($this->goFonHomeServers[$this->goFonHomeServer])){ + $str = $this->SQL_remove_me(true); + if($str){ + print_red($str); + return false; + } + }else{ + print_red(_("Could not remove the conference entry from database on home server (%s). Please check your asterisk database configuration.")); + return false; + } + /* Remove ldap entry */ $ldap= $this->config->get_ldap_link(); $ldap->cd ($this->dn); $ldap->recursive_remove(); - show_ldap_error($ldap->get_error(), sprintf(_("Removing of goFonConference/generic with dn '%s' failed."),$this->dn)); /* Optionally execute a command after we're done */ $this->handle_post_events('remove'); } @@ -218,15 +274,14 @@ class conference extends plugin $this->base= $_POST['base']; } } - foreach(array("goFonConferenceOption_P","goFonConferenceOption_r","goFonConferenceOption_M","goFonConferenceOption_s", "goFonConferenceOption_i","goFonConferenceOption_c","goFonConferenceOption_D") as $attrs){ /* Acl can't contain _ so we remove it here. */ $acl_name = preg_replace("/_/","",$attrs); - /* Attribute writeable ? */ if($this->acl_is_writeable($acl_name)){ + if(isset($_POST[$attrs])){ $this->$attrs = $_POST[$attrs]; }else{ @@ -238,6 +293,52 @@ class conference extends plugin } + function check_database_accessibility() + { + /* Check if mysql extension is available */ + if(!is_callable("mysql_pconnect")){ + return(_("Can't save any changes to asterisk database, there is currently no mysql extension available in your php setup.")); + } + + /******************** + * Check currently selected home server + ********************/ + + $cfg_Current = $this->goFonHomeServers[$this->goFonHomeServer]; + $r_current = @mysql_pconnect($cfg_Current['SERVER'],$cfg_Current['LOGIN'],$cfg_Current['PASSWORD']); + if(!$r_current){ + gosa_log(@mysql_error($r_current)); + return(sprintf(_("The MySQL home server '%s' isn't reachable as user '%s', check GOsa log for mysql error."), + $cfg_Current['SERVER'],$cfg_Current['LOGIN'])); + } + $db_current = @mysql_select_db($cfg_Current['DB'],$r_current); + if(!$db_current){ + gosa_log(@mysql_error($r_current)); + mysql_close($r_current); + return( sprintf(_("Can't select database '%s' on home server '%s'."),$cfg_Current['DB'],$cfg_Current['SERVER'])); + } + + /******************** + * Check init home server + ********************/ + + if($this->goFonHomeServers != $this->init_HomeServer){ + $cfg_Init = $this->goFonHomeServers[$this->init_HomeServer] ; + $r_init = @mysql_pconnect($cfg_Init['SERVER'],$cfg_Init['LOGIN'],$cfg_Init['PASSWORD']); + if(!$r_init){ + gosa_log(@mysql_error($r_init)); + return(sprintf(_("The MySQL initial home server '%s' isn't reachable as user '%s', check GOsa log for mysql error."), + $cfg_Init['SERVER'],$cfg_Init['LOGIN'])); + } + $db_init = @mysql_select_db($cfg_Init['DB'],$r_init); + if(!$db_init){ + gosa_log(@mysql_error($r_init)); + mysql_close($r_init); + return( sprintf(_("Can't select database '%s' on initial home server '%s'."),$cfg_Init['DB'],$cfg_Init['SERVER'])); + } + } + } + /* Check values */ function check() { @@ -248,8 +349,10 @@ class conference extends plugin $message[] = $this->is_number_used(); } - if((!empty($this->goFonPIN)||($this->goFonConferenceOption_P=="P"))&&($this->goFonConferenceOption_D=="d")){ - $message[] =_("You have specified a conference 'without PIN' ... please leave the PIN fields empty."); + /* Check if previously selected server is still available */ + if($this->initially_was_account && !isset($this->goFonHomeServers[$this->goFonHomeServer])){ + $message[]= sprintf(_("The previously selected asterisk home server (%s) is no longer available."),preg_replace("/,/",", ",$this->goFonHomeServer)); + return($message); } if((empty($this->goFonPIN))&&($this->goFonConferenceOption_P=="P")&&($this->goFonConferenceOption_D=="D")){ @@ -268,62 +371,81 @@ class conference extends plugin $message[] =_("Only numbers are allowed in Lifetime."); } - $this->SQL_remove_me(false); - $this->SQL_add_me(false); - - if(!empty($this->generate_error)){ - $message[]=$this->generate_error; - $this->generate_error=""; + /* Check if add could be successful */ + $str = $this->SQL_add_me(false); + if(!empty($str)){ + $message[] = $str; } return $message; } - function SQL_add_me($save){ - - if(!isset($_SESSION['config']->data['SERVERS']['FON'][0])){ - $this->generate_error = _("There is currently no asterisk server defined. Possibly you are missing a server that handles the asterisk management (goFonServer). Your settings can't be saved to asterisk database."); - return(false); + function SQL_add_me($save) + { + /* Check if there is at least on server configuration */ + if(!count($this->goFonHomeServers)){ + return( _("There is currently no asterisk server defined. Possibly you are missing a server that handles the asterisk management (goFonServer). Your settings can't be saved to asterisk database.")); } - // Get Configuration for Mysql database Server - $a_SETUP = $_SESSION['config']->data['SERVERS']['FON'][0]; - - // Connect to DB server - $r_con = false; - - if(!is_callable("mysql_pconnect")){ - if(!$this->error_shown){ - print_red(_("Can't save any changes to asterisk database, there is currently no mysql extension available in your php setup.")); - $this->error_shown = true; - } - return(true); + /******************** + * Get configuration and try to connect + ********************/ + + /* Check if databases are reachable, returns an error string if anything fails */ + $error_str = $this->check_database_accessibility(); + if($error_str){ + return($error_str); } - $r_con = @mysql_pconnect($a_SETUP['SERVER'],$a_SETUP['LOGIN'],$a_SETUP['PASSWORD']); - // Check if we are connected correctly - if(!$r_con){ - $this->generate_error = sprintf(_("The MySQL server '%s' isn't reachable as user '%s', check GOsa log for mysql error."), - $a_SETUP['SERVER'],$a_SETUP['LOGIN']); - gosa_log(mysql_error()); - return false; + /* Remove old entries, returns an error string if anything fails */ + $error_str = $this->SQL_remove_me($save); + if($error_str){ + return($error_str); } - // Select database for Extensions - $db = @mysql_select_db($a_SETUP['DB'],$r_con); + /* Connect to current database to be able to add new entries */ + $cfg_Current = $this->goFonHomeServers[$this->goFonHomeServer] ; + $res_cur = @mysql_pconnect($cfg_Current['SERVER'],$cfg_Current['LOGIN'],$cfg_Current['PASSWORD']); + $db_cur = @mysql_select_db($cfg_Current['DB'],$res_cur); - // Test if we have the database selected correctly - if(!$db){ - $this->generate_error = sprintf(_("Can't select database %s on %s."),$a_SETUP['DB'],$a_SETUP['SERVER']); - gosa_log(mysql_error()); - return false; + /******************** + * Remove entries that could cause trouble + ********************/ + + /* If the current home server is different to the initial home server, + * there may be already some entries with the given telephoneNumber and/or cn. + * We must remove those entries to avoid duplicate use of the same extension name. + */ + if($this->goFonHomeServer != $this->init_HomeServer){ + $query = "SELECT id FROM ".$cfg_Current['EXT_TABLE']." WHERE exten='".$this->telephoneNumber."' OR '".$this->cn."';"; + $res = @mysql_query($query,$res_cur); + if(!$res){ + gosa_log(@mysql_error($res_cur)); + return(_("Can not check if there are already some entries with given telephone number and/or cn in the destination home server."). + " "._("Please have a look a the gosa logfiles.")); + } + if($save && mysql_affected_rows($res_cur)) { + $SQL = "DELETE FROM ".$cfg_Current['EXT_TABLE']." + WHERE (exten='".$this->telephoneNumber."') + OR (exten='".$this->cn."')"; + + /* Query and ensure that everything went fine */ + $res = @mysql_query($SQL,$res_cur); + if(!$res){ + gosa_log(@mysql_error($res_cur)); + return(_("Can not remove entries with some telephone number and/or cn from destination home server."). + " "._("Please have a look a the gosa logfiles.")); + } + } } + /******************** + * Add new conference entry + ********************/ if((!empty($this->telephoneNumber))&&($save==true)){ - $EXT=array(); - + /* Create string out of conference Flags */ $parameter =""; foreach(array("goFonConferenceOption_P","goFonConferenceOption_r","goFonConferenceOption_M","goFonConferenceOption_s", "goFonConferenceOption_i","goFonConferenceOption_c","goFonConferenceOption_D") as $attrs){ @@ -331,6 +453,7 @@ class conference extends plugin } $i=1; + $EXT=array(); $context="GOsa"; // Set Language to German $EXT[$i]['exten'] =$this->telephoneNumber; @@ -390,65 +513,64 @@ class conference extends plugin } $s_keys =preg_replace("/\,$/","",$s_keys); $s_values =preg_replace("/\,$/","",$s_values); - $SQL[]="INSERT INTO ".$a_SETUP['EXT_TABLE']." (".$s_keys.") VALUES (".$s_values.");"; + $SQL[]="INSERT INTO ".$cfg_Current['EXT_TABLE']." (".$s_keys.") VALUES (".$s_values.");"; } foreach($SQL as $sqlsyn){ - mysql_query($sqlsyn); + mysql_query($sqlsyn,$res_cur); } } - - @mysql_close($r_con); - return(true); + @mysql_close($res_cur); } - function SQL_remove_me($save){ - if(!is_callable("mysql_pconnect")){ - if(!$this->error_shown){ - print_red(_("Can't save any changes to asterisk database, there is currently no mysql extension available in your php setup.")); - $this->error_shown = true; - } - return(true); - } - - if($this->old_tele_number){ - // Get Configuration for Mysql database Server - $a_SETUP = $_SESSION['config']->data['SERVERS']['FON'][0]; - // Connect to DB server - $r_con = @mysql_pconnect($a_SETUP['SERVER'],$a_SETUP['LOGIN'],$a_SETUP['PASSWORD']); - - // Check if we are connected correctly - if(!$r_con){ - $this->generate_error = sprintf(_("The MySQL Server '%s' isn't reachable as user '%s', check GOsa log for mysql error."), - $a_SETUP['SERVER'],$a_SETUP['LOGIN']); - gosa_log(mysql_error()); - return false; - } - - // Select database for Extensions - $db = @mysql_select_db($a_SETUP['DB'],$r_con); - - // Test if we have the database selected correctly - if(!$db){ - $this->generate_error = sprintf(_("Can't select database %s on %s."),$a_SETUP['DB'],$a_SETUP['SERVER']); - gosa_log(mysql_error()); - return false; - } + /* Remove initial entry from database + * This function checks if there is an entry in the + * initial home server that uses this->old_cn or $this->old_tele_number + * and removes this entries. + * This function is called from save and remove_from parent. + * + * The parameter '$save' is false if we just + * want to check if a remove is possible. + * And true if we realy want to remove the entries. + */ + function SQL_remove_me($save) + { + /* check database access */ + $str = $this->check_database_accessibility(); + if($str){ + return($str); + } - $SQL = "DELETE FROM ".$a_SETUP['EXT_TABLE']." + /* Connect to old database */ + $cfg_Init = $this->goFonHomeServers[$this->init_HomeServer] ; + $r_init = @mysql_pconnect($cfg_Init['SERVER'],$cfg_Init['LOGIN'],$cfg_Init['PASSWORD']); + $db_init = @mysql_select_db($cfg_Init['DB'],$r_init); + + /* Check if there is an old entry */ + $query = "SELECT id FROM ".$cfg_Init['EXT_TABLE']." WHERE exten='".$this->old_tele_number."' OR '".$this->old_cn."';"; + $res = @mysql_query($query,$r_init); + if(!$res){ + gosa_log(@mysql_error($r_init)); + return(_("Can not check if entry exists in old database. Please have a look a the gosa logfiles.")); + } + + /* There are entries using this cn and/or phone number */ + if($save && mysql_affected_rows($r_init)) { + $SQL = "DELETE FROM ".$cfg_Init['EXT_TABLE']." WHERE (exten='".$this->old_tele_number."') - OR (exten='".$this->telephoneNumber."') - OR (exten='".$this->cn."') OR (exten='".$this->old_cn."')"; - - if($save){ - @mysql_query($SQL); - } + + /* Query and ensure that everything went fine */ + $res = @mysql_query($SQL,$r_init); + if(!$res){ + gosa_log(@mysql_error($r_init)); + return(_("Can not remove old entries from initial home server. Please have a look a the gosa logfiles.")); + } }//ENDE old num availiable ... - @mysql_close($r_con); - return(true); + @mysql_close($r_init); + return(false); } @@ -479,9 +601,6 @@ class conference extends plugin } - - - /* Save to LDAP */ function save() { @@ -492,13 +611,17 @@ class conference extends plugin $pin_use = true; } + /* Unset PIN if this is a conference without PIN */ + if((!empty($this->goFonPIN)||($this->goFonConferenceOption_P=="P"))&&($this->goFonConferenceOption_D=="d")){ + $this->goFonPIN = ""; + } + plugin::save(); if(empty($this->old_tele_number)){ $this->old_tele_number= $this->telephoneNumber; } - $this->SQL_remove_me(true); $this->SQL_add_me(true); if(empty($this->goFonConferenceOption_P)){ @@ -543,7 +666,6 @@ class conference extends plugin $this->postcreate(); } - /* Return plugin informations for acl handling */ function plInfo() { @@ -555,14 +677,15 @@ class conference extends plugin "plPriority" => 0, "plSection" => array("addons" => _("Addons")), "plCategory" => array("gofonconference" => array("description" => _("GOfon conference"), - "objectClass" => "gofonConference")), + "objectClass" => "gofonConference")), "plProvidedAcls" => array( "cn" => _("Name"), - "base" => _("Base"), - "description" => _("Description"), + "base" => _("Base"), + "description" => _("Description"), "goFonPIN" => _("Conference PIN"), + "goFonHomeServer" => _("Home server"), "goFonConferenceOptionP" => _("Preset PIN"), "goFonConferenceOptionr" => _("Record conference"), "goFonConferenceOptionM" => _("Play music on hold"), @@ -577,6 +700,8 @@ class conference extends plugin "goFonConferenceOwner" => _("Owner")) )); } + + } // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler: diff --git a/plugins/gofon/conference/generic.tpl b/plugins/gofon/conference/generic.tpl index 5ba587b0a..102917120 100644 --- a/plugins/gofon/conference/generic.tpl +++ b/plugins/gofon/conference/generic.tpl @@ -64,8 +64,17 @@ + + {$must} + +{render acl=$goFonHomeServerACL} + +{/render} + + -   diff --git a/plugins/gofon/phoneaccount/class_phoneAccount.inc b/plugins/gofon/phoneaccount/class_phoneAccount.inc index 81c46482a..9235dfd44 100644 --- a/plugins/gofon/phoneaccount/class_phoneAccount.inc +++ b/plugins/gofon/phoneaccount/class_phoneAccount.inc @@ -996,9 +996,14 @@ class phoneAccount extends plugin } } + /* Assign acls */ + $tmp = $this->plInfo(); + foreach($tmp['plProvidedAcls'] as $name => $translation){ + $smarty->assign($name."ACL",$this->getacl($name)); + } + /* Transfer ACL's */ foreach($this->attributes as $val){ - $smarty->assign($val."ACL",$this->getacl($val,$SkipWrite)); if(isset($this->$val)){ $smarty->assign($val,$this->$val); }else{ @@ -1453,6 +1458,7 @@ class phoneAccount extends plugin "plProvidedAcls" => array( "telephoneNumber" => _("Telephone number"), + "goFonHomeServer" => _("Home server"), "goFonMacro" => _("Macro settings"), "goFonHardware" => _("Phone hardware"), "goFonPIN" => _("Telephone pin"), -- 2.30.2