diff --git a/app/calls/call_edit.php b/app/calls/call_edit.php
index b9b149b954..33934c39dd 100644
--- a/app/calls/call_edit.php
+++ b/app/calls/call_edit.php
@@ -63,7 +63,7 @@ else {
$sql = "select * from v_extensions ";
$sql .= "where domain_uuid = '$domain_uuid' ";
$sql .= "and extension_uuid = '$extension_uuid' ";
- if (!(if_group("admin") || if_group("superadmin"))) {
+ if (!(permission_exists('follow_me') || permission_exists('call_forward') || permission_exists('do_not_disturb'))) {
if (count($_SESSION['user']['extension']) > 0) {
$sql .= "and (";
$x = 0;
diff --git a/app/devices/app_languages.php b/app/devices/app_languages.php
index 75ecf92aa3..577792fd96 100644
--- a/app/devices/app_languages.php
+++ b/app/devices/app_languages.php
@@ -1690,7 +1690,7 @@ $text['label-blf']['ar-eg'] = "";
$text['label-blf']['he'] = "";
$text['label-callers']['en-us'] = "Callers";
-$text['label-callers']['es-cl'] = "Llaamadas";
+$text['label-callers']['es-cl'] = "Llamadas";
$text['label-callers']['pt-pt'] = "";
$text['label-callers']['fr-fr'] = "";
$text['label-callers']['pt-br'] = "";
diff --git a/app/ring_groups/app_languages.php b/app/ring_groups/app_languages.php
index 41e87e22f2..4ce8421826 100644
--- a/app/ring_groups/app_languages.php
+++ b/app/ring_groups/app_languages.php
@@ -88,6 +88,17 @@ $text['option-rollover']['uk'] = "";
$text['option-rollover']['de-at'] = "Überrollen";
$text['option-rollover']['he'] = "";
+$text['option-random']['en-us'] = "Random";
+$text['option-random']['es-cl'] = "Aleatorio";
+$text['option-random']['pt-pt'] = "";
+$text['option-random']['fr-fr'] = "";
+$text['option-random']['pt-br'] = "";
+$text['option-random']['pl'] = "";
+$text['option-random']['sv-se'] = "";
+$text['option-random']['uk'] = "";
+$text['option-random']['de-at'] = "";
+$text['option-random']['he'] = "";
+
$text['option-ptring']['en-us'] = "pt-ring";
$text['option-ptring']['es-cl'] = "pt-ring";
$text['option-ptring']['fr-fr'] = "Portugal";
diff --git a/app/ring_groups/ring_group_edit.php b/app/ring_groups/ring_group_edit.php
index 9d7083e70e..929f7086d6 100644
--- a/app/ring_groups/ring_group_edit.php
+++ b/app/ring_groups/ring_group_edit.php
@@ -499,6 +499,7 @@ else {
echo " \n";
echo " \n";
echo " \n";
+ echo " \n";
echo " \n";
echo "
\n";
echo $text['description-strategy']."\n";
diff --git a/app/voicemail_greetings/voicemail_greetings.php b/app/voicemail_greetings/voicemail_greetings.php
index 623a832287..865c66353a 100644
--- a/app/voicemail_greetings/voicemail_greetings.php
+++ b/app/voicemail_greetings/voicemail_greetings.php
@@ -46,7 +46,7 @@ require_once "resources/check_auth.php";
}
//deny access if the user extension is not assigned
- if (!(if_group("superadmin") || if_group("admin"))) {
+ if (!permission_exists('voicemail_greeting_view')) {
if (!is_extension_assigned($voicemail_id)) {
echo "access denied";
return;
diff --git a/resources/install/scripts/app/ring_groups/index.lua b/resources/install/scripts/app/ring_groups/index.lua
index 0cbc8832f5..a8c0b8e266 100644
--- a/resources/install/scripts/app/ring_groups/index.lua
+++ b/resources/install/scripts/app/ring_groups/index.lua
@@ -158,6 +158,31 @@
--forward the ring group
session:execute("transfer", ring_group_forward_destination.." XML "..context);
else
+ --get the strategy of the ring group, if random, we use random() to order the destinations
+ sql = [[
+ SELECT
+ r.ring_group_strategy
+ FROM
+ v_ring_groups as r
+ WHERE
+ ring_group_uuid = ']]..ring_group_uuid..[['
+ AND r.domain_uuid = ']]..domain_uuid..[['
+ AND r.ring_group_enabled = 'true'
+ ]];
+
+
+ assert(dbh:query(sql, function(row)
+ if (row.ring_group_strategy == "random") then
+ if (database["type"] == "mysql") then
+ sql_order = 'rand()'
+ else
+ sql_order = 'random()' --both postgresql and sqlite uses random() instead of rand()
+ end
+ else
+ sql_order='d.destination_delay, d.destination_number asc'
+ end
+ end));
+
--get the ring group destinations
sql = [[
SELECT
@@ -172,7 +197,7 @@
AND r.domain_uuid = ']]..domain_uuid..[['
AND r.ring_group_enabled = 'true'
ORDER BY
- d.destination_delay, d.destination_number asc
+ ]]..sql_order..[[
]];
--freeswitch.consoleLog("notice", "SQL:" .. sql .. "\n");
destinations = {};
@@ -291,6 +316,9 @@
if (ring_group_strategy == "sequence") then
delimiter = "|";
end
+ if (ring_group_strategy == "random") then
+ delimiter = "|";
+ end
if (ring_group_strategy == "simultaneous") then
delimiter = ",";
end
diff --git a/resources/install/scripts/app/xml_handler/resources/scripts/dialplan/dialplan.lua b/resources/install/scripts/app/xml_handler/resources/scripts/dialplan/dialplan.lua
index c838fcf782..5ccc18f06b 100644
--- a/resources/install/scripts/app/xml_handler/resources/scripts/dialplan/dialplan.lua
+++ b/resources/install/scripts/app/xml_handler/resources/scripts/dialplan/dialplan.lua
@@ -24,15 +24,22 @@
-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
+ local cache = require"resources.functions.cache"
+ local log = require"resources.functions.log"["xml_handler"]
+
--get the cache
- if (trim(api:execute("module_exists", "mod_memcache")) == "true") then
- XML_STRING = trim(api:execute("memcache", "get dialplan:" .. call_context));
- else
- XML_STRING = "-ERR NOT FOUND";
+ XML_STRING, err = cache.get("dialplan:" .. call_context)
+
+ if debug['cache'] then
+ if XML_STRING then
+ log.notice("dialplan:"..call_context.." source: memcache");
+ elseif err ~= 'NOT FOUND' then
+ log.notice("error get element form cache: " .. err);
+ end
end
--set the cache
- if (XML_STRING == "-ERR NOT FOUND") then
+ if not XML_STRING then
--connect to the database
require "resources.functions.database_handle";
@@ -99,7 +106,7 @@
sql = sql .. "ELSE 100 END, ";
sql = sql .. "s.dialplan_detail_order asc ";
if (debug["sql"]) then
- freeswitch.consoleLog("notice", "[xml_handler] SQL: " .. sql .. "\n");
+ log.notice("SQL: " .. sql);
end
x = 0;
dbh:query(sql, function(row)
@@ -287,8 +294,9 @@
XML_STRING = table.concat(xml, "\n");
--set the cache
- tmp = XML_STRING:gsub("\\", "\\\\");
- result = trim(api:execute("memcache", "set dialplan:" .. call_context .. " '"..tmp:gsub("'", "'").."' "..expire["dialplan"]));
+ if cache.support() then
+ cache.set("dialplan:" .. call_context, XML_STRING, expire["dialplan"])
+ end
--send the xml to the console
if (debug["xml_string"]) then
@@ -299,17 +307,10 @@
--send to the console
if (debug["cache"]) then
- freeswitch.consoleLog("notice", "[xml_handler] dialplan:"..call_context.." source: database\n");
+ log.notice("dialplan:"..call_context.." source: database");
end
--close the database connection
dbh:release();
- else
- --replace the ' back to a single quote
- XML_STRING = XML_STRING:gsub("'", "'");
-
- --send to the console
- if (debug["cache"]) then
- freeswitch.consoleLog("notice", "[xml_handler] dialplan:"..call_context.." source: memcache\n");
- end
end
+
diff --git a/resources/install/scripts/resources/functions/cache.lua b/resources/install/scripts/resources/functions/cache.lua
new file mode 100644
index 0000000000..29c1b9d57e
--- /dev/null
+++ b/resources/install/scripts/resources/functions/cache.lua
@@ -0,0 +1,68 @@
+-- @usage cache = require "resources.functions.cache"
+-- value = cache.get(key)
+-- if not value then
+-- ...
+-- cache.set(key, value, expire)
+-- end
+--
+
+require "resources.functions.trim";
+
+local api = api or freeswitch.API();
+
+local Cache = {}
+
+local function check_error(result)
+ result = trim(result or '')
+
+ if result and result:sub(1, 4) == '-ERR' then
+ return nil, trim(result:sub(5))
+ end
+
+ if result == 'INVALID COMMAND!' and not Cache.support() then
+ return nil, 'INVALID COMMAND'
+ end
+
+ return result
+end
+
+function Cache.support()
+ -- assume it is not unloadable
+ if Cache._support then
+ return true
+ end
+ Cache._support = (trim(api:execute('module_exists', 'mod_memcache')) == 'true')
+ return Cache._support
+end
+
+--- Get element from cache
+--
+-- @tparam key string
+-- @return[1] string value
+-- @return[2] nil
+-- @return[2] error string `e.g. 'NOT FOUND'
+-- @note error string does not contain `-ERR` prefix
+function Cache.get(key)
+ local result, err = check_error(api:execute('memcache', 'get ' .. key))
+ if not result then return nil, err end
+ return (result:gsub("'", "'"))
+end
+
+function Cache.set(key, value, expire)
+ value = value:gsub("'", "'"):gsub("\\", "\\\\")
+ expire = expire and tostring(expire) or ""
+ return check_error(api:execute("memcache", "set " .. key .. " '" .. value .. "' " .. expire))
+end
+
+function Cache.del(key)
+ local result, err = check_error(api:execute("memcache", "set " .. key .. " '" .. value .. "' " .. expire))
+ if not result then
+ if err == 'NOT FOUND' then
+ return true
+ end
+ return nil, err
+ end
+ return result == '+OK'
+end
+
+return Cache
diff --git a/resources/install/scripts/resources/functions/log.lua b/resources/install/scripts/resources/functions/log.lua
new file mode 100644
index 0000000000..b6cb59652b
--- /dev/null
+++ b/resources/install/scripts/resources/functions/log.lua
@@ -0,0 +1,70 @@
+-- @usage local log = require"resources.functions.log"["xml_handler"]
+-- log.notice("hello world")
+-- log.noticef("%s %s", "hello", "world")
+-- -- log if debug.SQL or debug.xml_handler.SQL then
+-- log.tracef("SQL", "SQL is %s", sql)
+
+local function log(name, level, msg)
+ freeswitch.consoleLog(level, "[" .. name .. "] " .. msg .. "\n")
+end
+
+local function logf(name, level, ...)
+ return log(name, level, string.format(...))
+end
+
+local function trace(type, name, ...)
+ local t = debug[name]
+ if t and t[type] ~= nil then
+ if t[type] then
+ return log(name, ...)
+ end
+ end
+ if debug[type] then
+ log(name, ...)
+ end
+end
+
+local function tracef(type, name, level, ...)
+ local t = debug[name]
+ if t and t[type] ~= nil then
+ if t[type] then
+ return logf(name, ...)
+ end
+ end
+ if debug[type] then
+ logf(name, ...)
+ end
+end
+
+local LEVELS = {
+ 'error',
+ 'warning',
+ 'notice',
+ 'info',
+}
+
+local TRACE_LEVEL = 'notice'
+
+local function make_log(name)
+ local logger = {}
+ for i = 1, #LEVELS do
+ logger[ LEVELS[i] ] = function(...) return log(name, LEVELS[i], ...) end;
+ logger[ LEVELS[i] .. "f" ] = function(...) return logf(name, LEVELS[i], ...) end;
+ end
+
+ logger.trace = function(type, ...)
+ trace(type, name, TRACE_LEVEL, ...)
+ end
+
+ logger.tracef = function(type, ...)
+ tracef(type, name, TRACE_LEVEL, ...)
+ end
+
+ return logger
+end
+
+return setmetatable({}, {__index = function(self, name)
+ local logger = make_log(name)
+ self[name] = logger
+ return logger
+end})
\ No newline at end of file
diff --git a/resources/install/scripts/ring_member.lua b/resources/install/scripts/ring_member.lua
new file mode 100644
index 0000000000..6fc0cedc62
--- /dev/null
+++ b/resources/install/scripts/ring_member.lua
@@ -0,0 +1,145 @@
+--
+-- FusionPBX
+-- Version: MPL 1.1
+--
+-- The contents of this file are subject to the Mozilla Public License Version
+-- 1.1 (the "License"); you may not use this file except in compliance with
+-- the License. You may obtain a copy of the License at
+-- http://www.mozilla.org/MPL/
+--
+-- Software distributed under the License is distributed on an "AS IS" basis,
+-- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+-- for the specific language governing rights and limitations under the
+-- License.
+--
+-- The Original Code is FusionPBX
+--
+-- The Initial Developer of the Original Code is
+-- Mark J Crane
+-- Copyright (C) 2010
+-- All Rights Reserved.
+--
+-- Contributor(s):
+-- Koldo A. Marcos
+
+
+--include config.lua
+ require "resources.functions.config";
+
+--connect to the database
+ require "resources.functions.database_handle";
+ dbh = database_handle('system');
+
+sounds_dir = "";
+recordings_dir = "";
+pin_number = "";
+max_tries = "3";
+digit_timeout = "3000";
+
+
+local random = math.random
+local function uuid()
+ local template ='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
+ return string.gsub(template, '[xy]', function (c)
+ local v = (c == 'x') and random(0, 0xf) or random(8, 0xb)
+ return string.format('%x', v)
+ end)
+end
+
+if ( session:ready() ) then
+ session:answer();
+ --session:execute("info", "");
+ destination_number = session:getVariable("user_name");
+ pin_number = session:getVariable("pin_number");
+ sounds_dir = session:getVariable("sounds_dir");
+ ring_group_uuid = session:getVariable("ring_group_uuid");
+
+ --get info for the ring group
+ sql = "SELECT * FROM v_ring_groups ";
+ sql = sql .. "where ring_group_uuid = '"..ring_group_uuid.."' ";
+ status = dbh:query(sql, function(row)
+ domain_uuid = row["domain_uuid"];
+ end);
+
+ destination_timeout = 15;
+ destination_delay = 0;
+
+
+
+ ring_group_destination_uuid = uuid();
+
+ --pin number is not required
+
+ --press 1 to login and 2 to logout
+ menu_selection = session:playAndGetDigits(1, 1, max_tries, digit_timeout, "#", "ivr/ivr-enter_destination_telephone_number.wav", "", "\\d+");
+ freeswitch.consoleLog("NOTICE", "menu_selection: "..menu_selection.."\n");
+ if (menu_selection == "1") then
+ --first, check to see if is already in that ring group
+ sql = [[
+ SELECT COUNT(*) AS in_group FROM
+ v_ring_group_destinations
+ WHERE
+ domain_uuid = ']]..domain_uuid..[['
+ AND ring_group_uuid = ']]..ring_group_uuid..[['
+ AND destination_number = ']]..destination_number..[['
+ ]];
+
+ --freeswitch.consoleLog("NOTICE", "ring_group_member: SQL "..sql.."\n");
+
+ assert(dbh:query(sql, function(row)
+ if (row.in_group == "0") then
+ sql = [[
+ INSERT INTO
+ v_ring_group_destinations
+ ( ring_group_destination_uuid,
+ domain_uuid,
+ ring_group_uuid,
+ destination_number,
+ destination_delay,
+ destination_timeout)
+ VALUES
+ ( ']]..ring_group_destination_uuid..[[',
+ ']]..domain_uuid..[[',
+ ']]..ring_group_uuid..[[',
+ ']]..destination_number..[[',
+ ]]..destination_delay..[[,
+ ]]..destination_timeout..[[)
+
+ ]];
+
+ --freeswitch.consoleLog("NOTICE", "ring_group_member: SQL "..sql.."\n");
+ dbh:query(sql);
+
+ freeswitch.consoleLog("NOTICE", "ring_group_member: LOG IN\n");
+ session:streamFile("ivr/ivr-you_are_now_logged_in.wav");
+
+ else
+ freeswitch.consoleLog("NOTICE", "ring_group_member: ALREADY LOGGED IN\n");
+ session:streamFile("ivr/ivr-you_are_now_logged_in.wav");
+ end
+ end));
+
+
+ end
+ if (menu_selection == "2") then
+ sql = [[
+ DELETE FROM
+ v_ring_group_destinations
+ WHERE
+ domain_uuid =']]..domain_uuid..[['
+ AND ring_group_uuid=']]..ring_group_uuid..[['
+ AND destination_number=']]..destination_number..[['
+ ]];
+ freeswitch.consoleLog("NOTICE", "ring_group_member: SQL "..sql.."\n");
+ dbh:query(sql);
+
+ freeswitch.consoleLog("NOTICE", "ring_group_member: LOG OUT\n");
+ session:streamFile("ivr/ivr-you_are_now_logged_out.wav");
+ end
+
+ --wait for the file to be written before proceeding
+ --session:sleep(1000);
+
+ --hangup
+ session:hangup();
+end