root/contributed_modules/modules/customcontexts/functions.inc.php

Revision 9995, 42.0 kB (checked in by p_lindheimer, 2 years ago)

fixes #4384 don't report blank destinations as that is legal and most common

Line 
1 <?php 
2 /* $Id:$ */
3
4 //used to get module information
5 function customcontexts_getmodulevalue($id) {
6 static $moduledisplayname = null;
7 static $moduleversion = null;
8 global $db;
9   switch ($id) {
10     case 'moduledisplayname':
11       if ($moduledisplayname === null) {
12         $module_info = module_getinfo('customcontexts');
13         $moduledisplayname = $module_info['customcontexts']['name'];
14       }
15       return $moduledisplayname;
16       break;
17     case 'moduleversion':
18       if ($moduleversion === null) {
19         $moduleversion = modules_getversion('customcontexts');
20       }
21       return $moduleversion;
22       break;
23     default:
24     $sql = "select value from customcontexts_module where id = '$id'";
25     $results = $db->getAll($sql);
26     if(DB::IsError($results)) {
27       $results = null;
28     }
29     return isset($results)?$results[0][0]:null;
30   }
31 }
32
33 //used to get module information
34 function customcontexts_setmodulevalue($id,$value) {
35   global $db;
36   $sql = "update customcontexts_module set value = '$value' where id = '$id'";
37   $db->query($sql);
38 }
39
40 //after dialplan is created and ready for injection, we grab the includes of any context the user added in admin
41 function customcontexts_hookGet_config($engine) {
42   global $db;
43   global $ext;
44   switch($engine) {
45     case 'asterisk':
46       $sql = 'UPDATE customcontexts_includes_list SET missing = 1 WHERE context
47               NOT IN (SELECT context FROM customcontexts_contexts_list WHERE locked = 1)';
48       $db->query($sql);
49       $sql = 'SELECT context FROM customcontexts_contexts_list';
50       $sections = $db->getAll($sql);
51       if(DB::IsError($results)) {
52         $results = null;
53       }
54       foreach ($sections as $section) {
55         $section = $section[0];
56         $i = 0;
57         if (isset($ext->_includes[$section])) {
58           foreach ($ext->_includes[$section] as $include) {
59             $i = $i + 1;
60             if ($section == 'outbound-allroutes') {
61               $sql = 'INSERT INTO customcontexts_includes_list
62                       (context, include, description, missing, sort)
63                       VALUES ("'.$section.'", "'.$include['include'].'",
64                       "'.$include['comment'].'", "0", "'.$i.'")
65                       ON DUPLICATE KEY UPDATE sort = "'.$i.'", missing = "0"';
66               $db->query($sql);
67             } else {
68               $sql = 'UPDATE customcontexts_includes_list SET missing = "0", sort = "'.$i.'"
69                       WHERE context = "'.$section.'" and include = "'.$include['include'].'"';
70               $db->query($sql);
71             }
72             $sql = 'INSERT IGNORE INTO customcontexts_includes_list
73                     (context, include, description, sort)
74                     VALUES ("'.$section.'", "'.$include['include'].'",
75                     "'.$include['include'].'", "'.$i.'")';
76             $db->query($sql);
77           }
78         }
79       }
80       $sql = "delete from  customcontexts_includes_list where missing = 1";
81       $db->query($sql);
82     break;
83   }
84 }
85
86 // provide hook for routing
87 function customcontexts_hook_core($viewing_itemid, $target_menuid) {
88   switch ($target_menuid) {
89     // only provide display for outbound routing
90     case 'routing':
91       /*$route = substr($viewing_itemid,4);$hookhtml = '';return $hookhtml;*/
92       return '';
93     break;
94     default:
95         return false;
96     break;
97   }
98 }
99
100 //this lists all includes from the sql database (for the requsted context) which we parsed out of the dialplan
101 //from any contexts the user entered in admin - information was saved to database on the last reload
102
103 function customcontexts_getincludeslist($context) {
104   global $db;
105 //  $sql = "select include, description from customcontexts_includes_list where context = '".$context."' order by description";
106   $sql = "SELECT include, customcontexts_includes_list.description,
107           COUNT(customcontexts_contexts_list.context) AS preemptcount 
108           FROM customcontexts_includes_list
109           LEFT OUTER JOIN customcontexts_contexts_list
110           ON include = customcontexts_contexts_list.context
111           WHERE customcontexts_includes_list.context = '$context'
112           GROUP BY include, customcontexts_includes_list.description
113           ORDER BY customcontexts_includes_list.sort,
114           customcontexts_includes_list.description";
115   $results = $db->getAll($sql);
116   if(DB::IsError($results)) {
117     $results = null;
118   }
119   foreach ($results as $val) {
120     $tmparray[] = array($val[0], $val[1], $val[2]);
121   }
122   return $tmparray;
123 }
124
125 //lists any contexts the user entered in admin for us to parse for includes to make available to his custom contexts
126 function customcontexts_getcontextslist() {
127   global $db;
128   $sql = "select context, description from customcontexts_contexts_list order by description";
129   $results = $db->getAll($sql);
130   if(DB::IsError($results)) {
131     $results = null;
132   }
133   foreach ($results as $val) {
134     $tmparray[] = array($val[0], $val[1]);
135   }
136   return $tmparray;
137 }
138
139 //these are the users selections of includes for the selected custom context
140 function customcontexts_getincludes($context) {
141   global $db;
142 //  $sql = "select customcontexts_contexts_list.context, customcontexts_contexts_list.description as contextdescription, customcontexts_includes_list.include, customcontexts_includes_list.description, if(saved.include is null, 'no', if(saved.timegroupid is null, 'yes', saved.timegroupid)) as allow, saved.sort from customcontexts_contexts_list inner join customcontexts_includes_list on customcontexts_contexts_list.context = customcontexts_includes_list.context left outer join (select * from customcontexts_includes where context = '$context') saved on customcontexts_includes_list.include = saved.include order by customcontexts_contexts_list.description, customcontexts_includes_list.description";
143   $sql = "SELECT customcontexts_contexts_list.context,
144           customcontexts_contexts_list.description AS contextdescription,
145           customcontexts_includes_list.include,
146           customcontexts_includes_list.description,
147           IF(saved.include is null, 'no',
148             IF(saved.timegroupid is null, IF(saved.userules is null, 'yes', saved.userules),
149           saved.timegroupid)) AS allow,
150           IF(saved.sort is null,customcontexts_includes_list.sort,saved.sort) AS sort,
151           COUNT(preemptcheck.context) AS preemptcount FROM customcontexts_contexts_list
152           INNER JOIN customcontexts_includes_list
153           ON customcontexts_contexts_list.context = customcontexts_includes_list.context
154           LEFT OUTER JOIN (SELECT * from customcontexts_includes WHERE context = '$context')  AS saved
155           ON customcontexts_includes_list.include = saved.include
156           LEFT OUTER JOIN customcontexts_contexts_list preemptcheck
157           ON customcontexts_includes_list.include = preemptcheck.context
158           LEFT OUTER JOIN  outbound_route_sequence
159           ON REPLACE(customcontexts_includes_list.include,'outrt-','') = outbound_route_sequence.route_id
160           GROUP BY customcontexts_contexts_list.context,
161           customcontexts_contexts_list.description,
162           customcontexts_includes_list.include,
163           customcontexts_includes_list.description,
164           IF(saved.include is null, 'no',
165             IF(saved.timegroupid is null, 'yes', saved.timegroupid)),
166           saved.sort, 
167           customcontexts_contexts_list.description
168           ORDER BY
169           IF(saved.sort is null,201,saved.sort),
170           customcontexts_includes_list.sort,
171           outbound_route_sequence.seq,
172           customcontexts_contexts_list.description,
173           customcontexts_includes_list.description";
174   $results = sql($sql,'getAll');
175   foreach ($results as $val){
176     $tmparray[] = array($val[0], $val[1], $val[2], $val[3], $val[4], $val[5], $val[6]);
177   }
178   //0-context 1-contextdescription  2-include  3-description 4-allow 5-sort 6-preemptcount
179   return $tmparray;
180 }
181
182 //these are the users custom contexts
183 function customcontexts_getcontexts() {
184   global $db;
185   $tmparray = array();
186   $sql = "select context, description from customcontexts_contexts order by description";
187   $results = $db->getAll($sql);
188   if(DB::IsError($results)) {
189     $results = array();
190   }
191   foreach ($results as $val) {
192     $tmparray[] = array($val[0], $val[1]);
193   }
194   return $tmparray;
195 }
196
197 /*
198  * allow reload to get our config
199  * we add all user custom contexts ad include his selected includes
200  * also maybe allow the user to specify invalid destination
201  */
202 function customcontexts_get_config($engine) {
203   global $ext;
204   switch($engine) {
205     case 'asterisk':
206   global $db;
207   $sql = "SELECT context, dialrules, faildestination, featurefaildestination,
208           failpin, featurefailpin FROM customcontexts_contexts";
209   $results = $db->getAll($sql);
210   if(DB::IsError($results)) {
211     $results = null;
212   }
213   foreach ($results as $val) {
214     $context = $val[0];
215     //$ext->_exts[$context][] = null;
216     //partially stolen from outbound routing
217     $dialpattern = explode("\n",$val[1]);
218     if (!$dialpattern) {
219       $dialpattern = array();
220     }
221     foreach (array_keys($dialpattern) as $key) {
222       //trim it
223       $dialpattern[$key] = trim($dialpattern[$key]);
224      
225       // remove blanks
226       if ($dialpattern[$key] == "") {
227         unset($dialpattern[$key]);
228       }
229      
230       // remove leading underscores (we do that on backend)
231       if ($dialpattern[$key][0] == "_") {
232         $dialpattern[$key] = substr($dialpattern[$key],1);
233       }
234     }
235     // check for duplicates, and re-sequence
236     $dialpattern = array_values(array_unique($dialpattern));
237     if (is_array($dialpattern)) {
238       foreach ($dialpattern as $pattern) {
239         if (false !== ($pos = strpos($pattern,"|"))) {
240           // we have a | meaning to not pass the digits on
241           // (ie, 9|NXXXXXX should use the pattern _9NXXXXXX but only pass NXXXXXX, not the leading 9)
242          
243           $pattern = str_replace("|","",$pattern); // remove all |'s
244           $exten = "EXTEN:".$pos; // chop off leading digit
245         } else {
246           // we pass the full dialed number as-is
247           $exten = "EXTEN";
248         }
249        
250         if (!preg_match("/^[0-9*]+$/",$pattern)) {
251           // note # is not here, as asterisk doesn't recoginize it as a normal digit, thus it requires _ pattern matching
252          
253           // it's not strictly digits, so it must have patterns, so prepend a _
254           $pattern = "_".$pattern;
255         }
256         $ext->add($context,$pattern, '', new ext_goto('1','${'.$exten.'}',$context.'_rulematch'));
257       }
258     }
259     //switch to first line to deny all access when time group deleted
260     //$sql = "select include, time from customcontexts_includes left outer join customcontexts_timegroups_detail on  customcontexts_includes.timegroupid = customcontexts_timegroups_detail.timegroupid where context = '".$context."' and (customcontexts_includes.timegroupid is null or customcontexts_timegroups_detail.timegroupid is not null) order by sort";
261     $sql  = "SELECT include, time, userules, seq FROM customcontexts_includes
262             LEFT OUTER JOIN timegroups_details
263             ON  customcontexts_includes.timegroupid = timegroups_details.timegroupid
264             LEFT OUTER JOIN  outbound_route_sequence
265             ON REPLACE(include,'outrt-','') = outbound_route_sequence.route_id
266             WHERE context = '$context' ORDER BY sort, seq";
267     $results2 = $db->getAll($sql);
268     if(DB::IsError($results2)) {
269       $results2 = null;
270     }
271     foreach ($results2 as $inc) {
272       $time = isset($inc[1])?'|'.$inc[1]:'';
273       switch ($inc[2]) {
274         case 'allowmatch':
275           if (is_array($dialpattern)) {
276             $ext->addInclude($context.'_rulematch',$inc[0].$time);
277           }
278         break;
279         case 'denymatch':
280           $ext->addInclude($context,$inc[0].$time);
281         break;
282         default:
283           $ext->addInclude($context,$inc[0].$time);
284           if (is_array($dialpattern)) {
285             $ext->addInclude($context.'_rulematch',$inc[0].$time);
286           }
287         break;
288       }
289     }
290     //these go in funny "exten => s,1,Macro(hangupcall,)"
291     //i'd rather use the base extension class to type it normally, but there is a bug in the class see ticket http://www.freepbx.org/trac/ticket/1453
292     $ext->add($context,'s', '', new ext_macro('hangupcall'));
293     $ext->add($context,'h', '', new ext_macro('hangupcall'));
294     $ext->addInclude($context,$context.'_bad-number');
295     $ext->addInclude($context,'bad-number');
296     if (is_array($dialpattern)) {
297       $ext->add($context.'_rulematch','s', '', new ext_macro('hangupcall'));
298       $ext->add($context.'_rulematch','h', '', new ext_macro('hangupcall'));
299       $ext->addInclude($context.'_rulematch',$context.'_bad-number');
300       $ext->addInclude($context.'_rulematch','bad-number');
301     }
302     $ext->_exts[$context.'_bad-number'][] = null;
303     if (isset($val[2]) && (!$val[2] == '')) {
304       $goto = explode(',',$val[2]);
305       if (isset($val[4]) && ($val[4] <> '')) {
306         $ext->add($context.'_bad-number', '_X.', '', new ext_authenticate($val[4]));
307       }
308       $ext->add($context.'_bad-number', '_X.', '', new ext_goto($goto[2],$goto[1],$goto[0]));
309     }
310     if (isset($val[3]) && (!$val[3] == '')) {
311       $goto = explode(',',$val[3]);
312       if (isset($val[5]) && ($val[5] <> '')) {
313         $ext->add($context.'_bad-number', '_*.', '', new ext_authenticate($val[5]));
314       }
315       $ext->add($context.'_bad-number', '_*.', '', new ext_goto($goto[2],$goto[1],$goto[0]));
316     }
317   }
318   break;
319   }
320 }
321
322 // returns a associative arrays with keys 'destination' and 'description'
323 // it allows custom contexts to be chosen as destinations
324 //this may seem a bit strange, but it works simply it sends the user to the EXTEN he dialed (or IVR option) within the selected context
325 function customcontexts_destinations() {
326   $contexts =  customcontexts_getcontexts();
327   $extens[] = array('destination' => 'from-internal,${EXTEN},1', 'description' => 'Full Internal Access');
328   if (is_array($contexts)) {
329     foreach ($contexts as $r) {
330       $extens[] = array('destination' => $r[0].',${EXTEN},1', 'description' => $r[1]);
331     }
332   }
333
334   return $extens;
335 }
336
337 //brute force hoook to devices and extensions pages to inform the user that they can place these devices in their custom contexts
338 function customcontexts_configpageinit($dispnum) {
339
340   global $currentcomponent;
341   $extdisplay = isset($_REQUEST['extdisplay'])?$_REQUEST['extdisplay']:null;
342   if ($extdisplay == '') {
343     return true;
344   }
345
346   if ($dispnum == 'devices' || $dispnum == 'extensions') {
347     $device_info = core_devices_get($extdisplay);
348     if (empty($device_info)) {
349         return true;
350     } else {
351       $tech = $device_info['tech'];
352       switch ($tech) {
353         case 'iax2':
354         case 'iax':
355         case 'sip':
356         case 'dahdi':
357         case 'zap':
358           $_REQUEST['tech'] = $tech;
359           $_REQUEST['customcontext'] = $device_info['context'];
360         break;
361         default:
362           return true;
363       }
364     }
365   } else {
366     return true;
367   }
368
369   $contextssel  = customcontexts_getcontexts();
370   $currentcomponent->addoptlistitem('contextssel', 'from-internal', 'ALLOW ALL (Default)');
371   foreach ($contextssel as $val) {
372     $currentcomponent->addoptlistitem('contextssel', $val[0], $val[1]);
373   }
374   $currentcomponent->setoptlistopts('contextssel', 'sort', false);
375
376   switch ($dispnum) {
377     case 'devices':
378       $currentcomponent->addguifunc('customcontexts_devices_configpageload');
379     break;
380     case 'extensions':
381       $currentcomponent->addguifunc('customcontexts_extensions_configpageload');
382     break;
383   }
384 }
385
386 //hook gui function
387 function customcontexts_devices_configpageload() {
388   global $currentcomponent;
389
390   $extdisplay = isset($_REQUEST['extdisplay'])?$_REQUEST['extdisplay']:null;
391   $tech = $_REQUEST['tech'];
392   $curcontext = $_REQUEST['customcontext'];
393
394   $currentcomponent->addguielem('Device Options', new gui_selectbox('customcontext', $currentcomponent->getoptlist('contextssel'), $curcontext, 'Custom Context', 'You have the '.customcontexts_getmodulevalue('moduledisplayname').' Module installed! You can select a custom context from this list to limit this user to portions of the dialplan you defined in the '.customcontexts_getmodulevalue('moduledisplayname').' module.',true, "javascript:if (document.frm_devices.customcontext.value) {document.frm_devices.devinfo_context.value = document.frm_devices.customcontext.value} else {document.frm_devices.devinfo_context.value = 'from-internal'}"));
395
396   $js = '<script type="text/javascript">$(document).ready(function(){$("#devinfo_context").parent().parent().hide();});</script>';
397   $currentcomponent->addguielem('Device Options', new guielement('test-html', $js, ''));
398 }
399
400 //hook gui function
401 function customcontexts_extensions_configpageload() {
402   global $currentcomponent;
403
404   $extdisplay = isset($_REQUEST['extdisplay'])?$_REQUEST['extdisplay']:null;
405   $tech = $_REQUEST['tech'];
406   $curcontext = $_REQUEST['customcontext'];
407
408   $currentcomponent->addguielem('Device Options', new gui_selectbox('customcontext', $currentcomponent->getoptlist('contextssel'), $curcontext, 'Custom Context', 'You have the '.customcontexts_getmodulevalue('moduledisplayname').' Module installed! You can select a custom context from this list to limit this user to portions of the dialplan you defined in the '.customcontexts_getmodulevalue('moduledisplayname').' module.',true, "javascript:if (document.frm_extensions.customcontext.value) {document.frm_extensions.devinfo_context.value = document.frm_extensions.customcontext.value} else {document.frm_extensions.devinfo_context.value = 'from-internal'}"));
409
410   $js = '<script type="text/javascript">$(document).ready(function(){$("#devinfo_context").parent().parent().hide();});</script>';
411   $currentcomponent->addguielem('Device Options', new guielement('test-html', $js, ''));
412 }
413
414 /*
415  * admin page helper
416  * we are using gui styles so there is very little on the page
417  * the admin page is used to list _existing_ contexts for us to parse for includes
418  * these contexts/includes can be tagged with a description for the user to select on the custom contexts page
419  */
420 function customcontexts_customcontextsadmin_configpageinit($dispnum) {
421 global $currentcomponent;
422   switch ($dispnum) {
423     case 'customcontextsadmin':
424       $currentcomponent->addguifunc('customcontexts_customcontextsadmin_configpageload');
425       $currentcomponent->addprocessfunc('customcontexts_customcontextsadmin_configprocess', 5); 
426     break;
427   }
428 }
429
430 //this is the dirty work displaying the admin page
431 function customcontexts_customcontextsadmin_configpageload() {
432 global $currentcomponent;
433   $contexterr = 'Context may not be left blank and must contain only letters, numbers and a few select characters!';
434   $descerr = 'Description must be alpha-numeric, and may not be left blank';
435   $extdisplay = isset($_REQUEST['extdisplay'])?$_REQUEST['extdisplay']:null;
436   $action= isset($_REQUEST['action'])?$_REQUEST['action']:null;
437   if ($action == 'del') {
438     $currentcomponent->addguielem('_top', new gui_pageheading('title', _("Context").": $extdisplay"." deleted!", false), 0);
439   } else {
440     //need to get module name/type dynamically
441     $query = ($_SERVER['QUERY_STRING'])?$_SERVER['QUERY_STRING']:'type=tool&display=customcontextsadmin&extdisplay='.$extdisplay;
442     $delURL = $_SERVER['PHP_SELF'].'?'.$query.'&action=del';
443     $info = 'The context which contains includes which you would like to make available to '.customcontexts_getmodulevalue('moduledisplayname').'. Any context you enter here will be parsed for includes and you can then include them in your own '.customcontexts_getmodulevalue('moduledisplayname').'. Removing them here does NOT delete the context, rather makes them unavailable to your '.customcontexts_getmodulevalue('moduledisplayname').'.';
444     $currentcomponent->addguielem('_top', new gui_hidden('action', ($extdisplay ? 'edit' : 'add')));
445     $currentcomponent->addguielem('_bottom', new gui_link('help', _(customcontexts_getmodulevalue('moduledisplayname')." v".customcontexts_getmodulevalue('moduleversion')), 'http://www.freepbx.org/support/documentation/module-documentation/third-party-unsupported-modules/customcontexts', true, false), 0);
446     if (!$extdisplay) {
447       $currentcomponent->addguielem('_top', new gui_pageheading('title', _("Add Context"), false), 0);
448       $currentcomponent->addguielem('Context', new gui_textbox('extdisplay', '', 'Context', $info, 'isWhitespace() || !isFilename()', $contexterr, false), 3);
449       $currentcomponent->addguielem('Context', new gui_textbox('description', '', 'Description', 'This will display as a heading for the available includes on the '.customcontexts_getmodulevalue('moduledisplayname').' page.', '!isAlphanumeric() || isWhitespace()', $descerr, false), 3);
450     } else {
451       $savedcontext = customcontexts_customcontextsadmin_get($extdisplay);
452       $context = $savedcontext[0];
453       $description = $savedcontext[1];
454       $locked = $savedcontext[2];
455       $currentcomponent->addguielem('_top', new gui_hidden('extdisplay', $extdisplay));
456       $currentcomponent->addguielem('_top', new gui_pageheading('title', _("Edit Context").": $description", false), 0);
457       if ($locked == false) {     
458         $currentcomponent->addguielem('_top', new gui_link('del', _("Remove Context").": $context", $delURL, true, false), 0);
459       }
460       else
461       {
462         $currentcomponent->addguielem('_top', new gui_label('del', _("Context").": $context can not be removed!", $delURL, true, false), 0);
463       }
464       $currentcomponent->addguielem('Context', new gui_textbox('description', $description, 'Description', 'This will display as a heading for the available includes on the '.customcontexts_getmodulevalue('moduledisplayname').' page.', '!isAlphanumeric() || isWhitespace()', $descerr, false), 3);
465       $inclist = customcontexts_getincludeslist($extdisplay);
466       foreach ($inclist as $val) {
467         if ($val[2] > 0) {
468           $currentcomponent->addguielem('Includes Descriptions', new gui_textbox('includes['.$val[0].']', $val[1], '<font color="red"><strong>'.$val[0].'</strong></font>', 'This will display as the name of the include on the '.customcontexts_getmodulevalue('moduledisplayname').' page.<BR><font color="red"><strong>NOTE: This include should have a description denoting the fact that allowing it may allow another ENTIRE context!</strong></font>', '!isAlphanumeric() || isWhitespace()', $descerr, false), 3);
469         } else {
470           $currentcomponent->addguielem('Includes Descriptions', new gui_textbox('includes['.$val[0].']', $val[1], $val[0], 'This will display as the name of the include on the '.customcontexts_getmodulevalue('moduledisplayname').' page.', '!isAlphanumeric() || isWhitespace()', $descerr, false), 3);
471         }
472
473       }
474     }
475   }
476 }
477
478
479 //handle the admin submit button
480 function customcontexts_customcontextsadmin_configprocess() {
481   $action= isset($_REQUEST['action'])?$_REQUEST['action']:null;
482   $context= isset($_REQUEST['extdisplay'])?$_REQUEST['extdisplay']:null;
483   $description= isset($_REQUEST['description'])?$_REQUEST['description']:null;
484 //addslashes 
485   switch ($action) {
486   case 'add':
487     customcontexts_customcontextsadmin_add($context,$description);
488   break;
489   case 'edit':
490     customcontexts_customcontextsadmin_edit($context,$description);
491     $includes = isset($_REQUEST['includes'])?$_REQUEST['includes']:null;
492     customcontexts_customcontextsadmin_editincludes($context,$includes);
493   break;
494   case 'del':
495     customcontexts_customcontextsadmin_del($context);
496   break;
497   }
498 }
499
500
501 //retrieve a single context for the admin page
502 function customcontexts_customcontextsadmin_get($context) {
503   global $db;
504   $sql = "select context, description, locked from customcontexts_contexts_list where context = '$context'";
505   $results = $db->getAll($sql);
506   if(DB::IsError($results)) {
507     $results = null;
508   }
509   $tmparray = array($results[0][0], $results[0][1], $results[0][2]);
510   return $tmparray;
511 }
512
513 //add a single context for admin
514 function customcontexts_customcontextsadmin_add($context,$description) {
515   global $db;
516   $sql = "insert customcontexts_contexts_list (context, description) VALUES ('$context','$description')";
517   $db->query($sql);
518   needreload();
519 }
520
521 //del a single context from admin
522 function customcontexts_customcontextsadmin_del($context) {
523   global $db;
524   $sql = "delete from customcontexts_includes_list where context = '$context'";
525   $db->query($sql);
526   $sql = "delete from customcontexts_contexts_list where context = '$context'";
527   $db->query($sql);
528   needreload();
529 }
530
531 //update a single context for admin
532 function customcontexts_customcontextsadmin_edit($context,$description) {
533   global $db;
534   $sql = "update customcontexts_contexts_list set description = '$description' where context = '$context'";
535   $db->query($sql);
536   needreload();
537 }
538
539 //edit the includes under a single admin context
540 function customcontexts_customcontextsadmin_editincludes($context,$includes) {
541   global $db;
542   $sql = "delete from customcontexts_includes_list  where context = '$context'";
543   $db->query($sql);
544   foreach ($includes as $key=>$val) {
545     $sql = "insert customcontexts_includes_list (context, include, description) values ('$context','$key','$val')";
546     $db->query($sql);
547   }
548   needreload();
549 }
550
551 //---------------------------------------------
552
553 /* custom contexts page helper
554  * we are using gui styles so there is very little on the page
555  * the custom contexts page is used to create _new_ contexts for use in the dialplan
556  * these contexts can include any includes which were made available from admin
557  */
558 function customcontexts_customcontexts_configpageinit($dispnum) {
559 global $currentcomponent;
560   switch ($dispnum) {
561     case 'customcontexts':
562       $currentcomponent->addoptlistitem('includeyn', 'yes', 'Allow');
563       $currentcomponent->addoptlistitem('includeyn', 'no', 'Deny');
564       $currentcomponent->addoptlistitem('includeyn', 'allowmatch', 'Allow Rules');
565       $currentcomponent->addoptlistitem('includeyn', 'denymatch', 'Deny Rules');
566       $timegroups = timeconditions_timegroups_list_groups();
567       foreach ($timegroups as $val) {
568         $currentcomponent->addoptlistitem('includeyn', $val[0], $val[1]);
569       }
570       $currentcomponent->setoptlistopts('includeyn', 'sort', false);
571       for($i = 0; $i <= 200; $i++) {
572         $currentcomponent->addoptlistitem('includesort', $i - 50, $i);
573       }
574       $currentcomponent->addguifunc('customcontexts_customcontexts_configpageload');
575       $currentcomponent->addprocessfunc('customcontexts_customcontexts_configprocess', 5); 
576     break;
577   }
578 }
579
580 //actually render the custom contexts page
581 function customcontexts_customcontexts_configpageload() {
582 global $currentcomponent;
583   $contexterr = 'Context may not be left blank and must contain only letters, numbers and a few select characters!';
584   $descerr = 'Description must be alpha-numeric, and may not be left blank';
585   $extdisplay = isset($_REQUEST['extdisplay'])?$_REQUEST['extdisplay']:null;
586   $action= isset($_REQUEST['action'])?$_REQUEST['action']:null;
587   $showsort = isset($_REQUEST['showsort'])?$_REQUEST['showsort']:null;
588   if (isset($showsort) && $showsort <> customcontexts_getmodulevalue('displaysortforincludes')) {
589     customcontexts_setmodulevalue('displaysortforincludes', $showsort);
590   }
591   if ($action == 'del') {
592     $currentcomponent->addguielem('_top', new gui_pageheading('title', _("Context").": $extdisplay"." deleted!", false), 0);
593   } else {
594     //need to get page name/type dynamically
595     //caused trouble on dup or del after dup or rename
596     //$query = ($_SERVER['QUERY_STRING'])?$_SERVER['QUERY_STRING']:'type=setup&display=customcontexts&extdisplay='.$extdisplay;
597     $query = 'type=setup&display=customcontexts&extdisplay='.$extdisplay;
598     $delURL = $_SERVER['PHP_SELF'].'?'.$query.'&action=del';
599     $dupURL = $_SERVER['PHP_SELF'].'?'.$query.'&action=dup';
600     $info = 'The custom context to make will be available in your dialplan. These contexts can be used as a context for a device/extension to allow them limited access to your dialplan.';
601     $currentcomponent->addguielem('_bottom', new gui_link('ver', _(customcontexts_getmodulevalue('moduledisplayname')." v".customcontexts_getmodulevalue('moduleversion')), 'http://www.freepbx.org/support/documentation/module-documentation/third-party-unsupported-modules/customcontexts', true, false), 0);
602     if (!$extdisplay) {
603       $currentcomponent->addguielem('_top', new gui_pageheading('title', _("Add Context"), false), 0);
604       $currentcomponent->addguielem('Context', new gui_textbox('extdisplay', '', 'Context', $info, 'isWhitespace() || !isFilename()', $contexterr, false), 3);
605       $currentcomponent->addguielem('Context', new gui_textbox('description', '', 'Description', 'This will display as the name of this custom context.', '!isAlphanumeric() || isWhitespace()', $descerr, false), 3);
606     } else {
607       $savedcontext = customcontexts_customcontexts_get($extdisplay);
608       $context = $savedcontext[0];
609       $description = $savedcontext[1];
610       $rulestext = $savedcontext[2];
611       $faildest  = $savedcontext[3];
612       $featurefaildest  = $savedcontext[4];
613       $failpin  = $savedcontext[5];
614       $featurefailpin  = $savedcontext[6];
615       $currentcomponent->addguielem('_top', new gui_hidden('extdisplay', $extdisplay));
616       $currentcomponent->addguielem('_top', new gui_pageheading('title', _("Edit Context").": $description", false), 0);
617       //$currentcomponent->addguielem('_top', new gui_link('del', _("Delete Context")." $context", $delURL, true, false), 0);
618       $currentcomponent->addguielem('_top', new guielement('del', '<tr><td colspan ="2"><a href="'.$delURL.'" onclick="return confirm(\'Are you sure you want to delete '.$context.'?\')">Delete Context '.$context.'</a></td></tr>', ''),3);
619       $currentcomponent->addguielem('_top', new gui_link('dup', _("Duplicate Context")." $context", $dupURL, true, false), 3);
620       $showsort = customcontexts_getmodulevalue('displaysortforincludes');
621       if ($showsort == 1) {
622       //$sortURL = $_SERVER['PHP_SELF'].'?'.$query.'&showsort=0';
623       //$currentcomponent->addguielem('_top', new gui_link('showsort', "Hide Sort Option", $sortURL, true, false), 0);
624       } else {
625         $sortURL = $_SERVER['PHP_SELF'].'?'.$query.'&showsort=1';
626         $currentcomponent->addguielem('_top', new gui_link('showsort', "Show Sort Option", $sortURL, true, false), 0);
627       }
628       $currentcomponent->addguielem('Context', new gui_textbox('newcontext', $extdisplay, 'Context', $info, 'isWhitespace() || !isFilename()', $contexterr, false), 2);
629       $currentcomponent->addguielem('Context', new gui_textbox('description', $description, 'Description', 'This will display as the name of this custom context.', '', '', false), 2);
630       $ruledesc = 'If defined, you will have the option for each portion of the dialplan to Allow Rule, and that inclued will only be available if the number dialed matches these rules, or Deny Rule, and that include will only be available if the dialed number does NOT match these rules. You may use a pipe | to strip the preceeding digits.';
631       $ruleshtml = '<tr><td valign="top"><a href="#" class="info">Dial Rules<span>'.$ruledesc.'</span></a></td><td><textarea cols="20" rows="5" id="dialpattern" name="dialpattern">'.$rulestext.'</textarea></td></tr>';
632       $currentcomponent->addguielem('Context', new guielement('rulesbox',$ruleshtml,''), 3);
633
634       $currentcomponent->addguielem('Failover Destination', new gui_textbox('failpin', $failpin, 'PIN', 'Enter a numeric PIN to require authentication before continuing to destination.', '!isPINList()', 'PIN must be numeric!', true), 4);
635       $currentcomponent->addguielem('Feature Code Failover Destination', new gui_textbox('featurefailpin', $featurefailpin, 'PIN', 'Enter a numeric PIN to require authentication before continuing to destination.', '!isPINList()', 'PIN must be numeric!', true), 4);
636       $currentcomponent->addguielem('Failover Destination', new gui_drawselects('dest0', 0, $faildest, 'Failover Destination'));
637       $currentcomponent->addguielem('Feature Code Failover Destination', new gui_drawselects('dest1', 1, $featurefaildest, 'Failover Destination'));
638       $currentcomponent->addguielem('Set All', new gui_selectbox('setall', $currentcomponent->getoptlist('includeyn'), '', 'Set All To:', 'Choose allow to allow access to all includes, choose deny to deny access.',true,'javascript:for (i=0;i<document.forms[\'frm_customcontexts\'].length;i++) {if(document.forms[\'frm_customcontexts\'][i].type==\'select-one\' && document.forms[\'frm_customcontexts\'][i].name.indexOf(\'[allow]\') >= 0 ) {document.forms[\'frm_customcontexts\'][i].selectedIndex = document.forms[\'frm_customcontexts\'][\'setall\'].selectedIndex-1;}}'),2);
639       $inclist = customcontexts_getincludes($extdisplay);
640       foreach ($inclist as $val) {
641         if ($showsort == 1) {
642           if ($val[6] > 0) {
643             //$currentcomponent->addguielem($val[1], new gui_selectbox('includes['.$val[2].'][allow]', $currentcomponent->getoptlist('includeyn'), $val[4], '<font color="red"><strong>'.$val[3].'</strong></font>', $val[2].': Choose allow to allow access to this include, choose deny to deny access.<BR><font color="red"><strong>NOTE: Allowing this include may automatically allow another ENTIRE context!</strong></font>',false));
644             $gui1 = new gui_selectbox('includes['.$val[2].'][allow]',
645                         $currentcomponent->getoptlist('includeyn'), $val[4],
646                         '<font color="red"><strong>'.$val[3].'</strong></font>',
647                         $val[2].': Choose allow to allow access to this include, choose deny to deny access.<BR><font color="red"><strong>NOTE: Allowing this include may automatically allow another ENTIRE context!</strong></font>',false);
648           } else {
649             //$currentcomponent->addguielem($val[1], new gui_selectbox('includes['.$val[2].'][allow]', $currentcomponent->getoptlist('includeyn'), $val[4], $val[3], $val[2].': Choose allow to allow access to this include, choose deny to deny access.',false));
650             $gui1 = new gui_selectbox('includes['.$val[2].'][allow]',
651                     $currentcomponent->getoptlist('includeyn'), $val[4], $val[3],
652                     $val[2].': Choose allow to allow access to this include, choose deny to deny access.',false);
653           }
654           //$currentcomponent->addguielem($val[1], new gui_selectbox('includes['.$val[2].'][sort]', $currentcomponent->getoptlist('includesort'), $val[5], '<div align="right">Priority</div>', 'Choose a priority with which to sort this option. Lower numbers have a higher priority.',false));
655           $guisort = new gui_selectbox('includes['.$val[2].'][sort]', $currentcomponent->getoptlist('includesort'), $val[5], '<div align="right">Priority</div>', 'Choose a priority with which to sort this option. Lower numbers have a higher priority.',false);
656           $inchtml = '<tr><td colspan="2"><table width="100%"><tr><td></td><td width="50"></td></tr>'.$gui1->generatehtml().'</table></td><td><table>'.$guisort->generatehtml().'</table></td></tr>';
657           $currentcomponent->addguielem($val[1], new guielement('$val[0]',$inchtml,''),3);
658         } else {
659           if ($val[6] > 0) {
660             $currentcomponent->addguielem($val[1], new gui_selectbox('includes['.$val[2].'][allow]',
661                               $currentcomponent->getoptlist('includeyn'), $val[4],
662                               '<font color="red"><strong>'.$val[3].'</strong></font>', $val[2].': Choose allow to allow access to this include, choose deny to deny access.<BR><font color="red"><strong>NOTE: Allowing this include may automatically allow another ENTIRE context!</strong></font>',false));
663           } else {
664             $currentcomponent->addguielem($val[1], new gui_selectbox('includes['.$val[2].'][allow]',
665                               $currentcomponent->getoptlist('includeyn'), $val[4],
666                               $val[3], $val[2].': Choose allow to allow access to this include, choose deny to deny access.',false));
667           }
668         }
669       }
670     }
671   }
672        $currentcomponent->addguielem('_top', new gui_hidden('action', ($extdisplay ? 'edit' : 'add')));
673 }
674
675 //handle custom contexts page submit button
676 function customcontexts_customcontexts_configprocess() {
677   $action= isset($_REQUEST['action'])?$_REQUEST['action']:null;
678   $context= isset($_REQUEST['extdisplay'])?$_REQUEST['extdisplay']:null;
679   $newcontext= isset($_REQUEST['newcontext'])?$_REQUEST['newcontext']:null;
680   $description= isset($_REQUEST['description'])?$_REQUEST['description']:null;
681   $dialrules= isset($_REQUEST['dialpattern'])?$_REQUEST['dialpattern']:null;
682   $faildest= isset($_REQUEST["goto0"])?$_REQUEST[$_REQUEST['goto0'].'0']:null;
683   $featurefaildest= isset($_REQUEST["goto1"])?$_REQUEST[$_REQUEST['goto1'].'1']:null;
684   $failpin= isset($_REQUEST['failpin'])?$_REQUEST['failpin']:null;
685   $featurefailpin= isset($_REQUEST['featurefailpin'])?$_REQUEST['featurefailpin']:null;
686
687 //addslashes 
688   switch ($action) {
689   case 'add':
690     customcontexts_customcontexts_add($context,$description,$dialrules,$faildest,$featurefaildest,$failpin,$featurefailpin);
691   break;
692   case 'edit':
693     if ($context <> $newcontext) {
694       $_REQUEST['extdisplay'] = isset($_REQUEST['extdisplay'])?$newcontext:null;
695     }
696     customcontexts_customcontexts_edit($context,$newcontext,$description,$dialrules,$faildest,$featurefaildest,$failpin,$featurefailpin);
697     $includes = isset($_REQUEST['includes'])?$_REQUEST['includes']:null;
698     customcontexts_customcontexts_editincludes($context,$includes,$newcontext);
699   break;
700   case 'del':
701     customcontexts_customcontexts_del($context);
702     $_REQUEST['extdisplay'] = null;
703   break;
704   case 'dup':
705     $newcontext = customcontexts_customcontexts_duplicatecontext($context);
706     if ($context <> $newcontext) {
707       $_REQUEST['extdisplay'] = isset($_REQUEST['extdisplay'])?$newcontext:null;
708     }
709   break;
710   }
711 }
712
713 //retrieve a single custom context for the custom contexts page
714 function customcontexts_customcontexts_get($context) {
715   global $db;
716   $sql = "select context, description, dialrules, faildestination, featurefaildestination, failpin, featurefailpin from customcontexts_contexts where context = '$context'";
717   $results = $db->getAll($sql);
718   if(DB::IsError($results)) {
719     $results = null;
720   }
721   $tmparray = array($results[0][0], $results[0][1], $results[0][2], $results[0][3], $results[0][4], $results[0][5], $results[0][6]);
722   return $tmparray;
723 }
724
725 //add a new custom context for custom contexts page
726 function customcontexts_customcontexts_add($context,$description,$dialrules,$faildest,$featurefaildest,$failpin,$featurefailpin) {
727   global $db;
728   $sql = "insert customcontexts_contexts (context, description, dialrules, faildestination, featurefaildestination, failpin, featurefailpin) VALUES ('$context','$description','$dialrules','$faildest','$featurefaildest','$failpin','$featurefailpin')";
729   $db->query($sql);
730   needreload();
731 }
732
733 //delete a single custom context from the custom contexts page
734 function customcontexts_customcontexts_del($context) {
735   global $db;
736   $sql = "delete from customcontexts_includes where context = '$context'";
737   $db->query($sql);
738   $sql = "delete from customcontexts_contexts where context = '$context'";
739   $db->query($sql);
740   needreload();
741 }
742
743 //update a single custom context from the custom contexts page
744 function customcontexts_customcontexts_edit($context,$newcontext,$description,$dialrules,$faildest,$featurefaildest,$failpin,$featurefailpin) {
745   global $db;
746   if (!isset($newcontext) || ($newcontext == '')) {
747     $newcontext = $context;
748   }
749   $sql = "update customcontexts_contexts set context = '$newcontext', description = '$description', dialrules = '$dialrules', faildestination = '$faildest', featurefaildestination = '$featurefaildest', failpin = '$failpin', featurefailpin = '$featurefailpin' where context = '$context'";
750   $db->query($sql);
751   needreload();
752 }
753
754 //update the includes under a single custom context from the custom contexts page
755 function customcontexts_customcontexts_editincludes($context,$includes,$newcontext) {
756   global $db;
757   $sql = "delete from customcontexts_includes  where context = '$context'";
758   $db->query($sql);
759   if (!isset($newcontext) || ($newcontext == '')) {
760     $newcontext = $context;
761   }
762   foreach ($includes as $key=>$val) {
763     if ($val[allow] <> 'no') {
764       $timegroup = 'null';
765       $sort = 0;
766       $userules = null;
767       if (is_numeric($val[allow])) {
768         $timegroup = $val[allow];
769       } else {
770         if ($val[allow] <> 'yes') {
771           $userules = $val[allow];
772         }
773       }
774       if (is_numeric($val[sort])) {
775         $sort = $val[sort];
776       }
777       $sql = "insert customcontexts_includes (context, include, timegroupid, sort, userules) values ('$newcontext','$key', $timegroup, $sort, '$userules')";
778       $db->query($sql);
779     }
780   }
781   needreload();
782 }
783
784 function customcontexts_customcontexts_duplicatecontext($context) {
785   global $db;
786   $suffix = '_2';
787   $counter = 2;
788   $sql = "select description, dialrules, faildestination, featurefaildestination, failpin, featurefailpin from customcontexts_contexts  where context = '$context'";
789   $results = $db->getAll($sql);
790   if(DB::IsError($results)) {
791     $results = null;
792     return;
793   }
794   $description = $results[0][0];
795   $dialrules = $results[0][1];
796   $faildest = $results[0][2];
797   $featurefaildest = $results[0][3];
798   $failpin = $results[0][4];
799   $featurefailpin = $results[0][5];
800   $sql = "select count(*) from customcontexts_contexts  where context = '".$context.$suffix."' or description = '".$description.$suffix."'";
801   $results = $db->getAll($sql);
802   if(DB::IsError($results)) {
803     $results = null;
804   }
805   while ($results[0][0] > 0) {
806     $counter = $counter + 1;
807     $suffix = '_'.$counter;
808     $sql = "select count(*) from customcontexts_contexts  where context = '".$context.$suffix."' or description = '".$description.$suffix."'";
809     $results = $db->getAll($sql);
810     if(DB::IsError($results)) {
811       $results = null;
812     }
813   }
814   customcontexts_customcontexts_add($context.$suffix,$description.$suffix,$dialrules,$faildest,$featurefaildest,$failpin,$featurefailpin);
815   $includes = customcontexts_getincludes($context);
816   foreach ($includes as $val) {
817     $newincludes[$val[2]] = array("allow"=>"$val[4]", "sort"=>"$val[5]");
818   }
819   customcontexts_customcontexts_editincludes($context.$suffix,$newincludes,$context.$suffix);
820   needreload();
821   return $context.$suffix;
822 }
823
824 /* callback to Time Groups Module so it can display usage information
825    of specific groups
826  */
827 function customcontexts_timegroups_usage($group_id) {
828
829   $group_id = q($group_id);
830   $results = sql("SELECT DISTINCT context, timegroupid FROM customcontexts_includes WHERE timegroupid = $group_id","getAll",DB_FETCHMODE_ASSOC);
831   if (empty($results)) {
832     return array();
833   } else {
834     foreach ($results as $result) {
835       $usage_arr[] = array(
836         "url_query" => "display=customcontexts&extdisplay=".$result['context'],
837         "description" => sprintf(_("Custom Context: %s"),$result['context']),
838       );
839     }
840     return $usage_arr;
841   }
842 }
843
844 function customcontexts_check_destinations($dest=true) {
845   global $active_modules;
846
847   $destlist = array();
848   if (is_array($dest) && empty($dest)) {
849     return $destlist;
850   }
851   $sql = "SELECT context, description, faildestination, featurefaildestination FROM customcontexts_contexts";
852   if ($dest !== true) {
853     $sql .= " WHERE (faildestination in ('".implode("','",$dest)."') ) OR (featurefaildestination in ('".implode("','",$dest)."') )";
854   }
855   $results = sql($sql,"getAll",DB_FETCHMODE_ASSOC);
856
857   $type = isset($active_modules['customcontexts']['type'])?$active_modules['customcontexts']['type']:'setup';
858
859   foreach ($results as $result) {
860     $thisdest    = $result['faildestination'];
861     // blank destinations in custom context are valid
862     if (!$thisdest) {
863       continue;
864     }
865     $thisid      = $result['context'];
866     $description = sprintf(_("Custom Context: %s (%s)"),$result['description'],$result['context']);
867     $thisurl     = 'config.php?display=customcontexts&extdisplay='.urlencode($thisid);
868     if ($dest === true || $dest = $thisdest) {
869       $destlist[] = array(
870         'dest' => $thisdest,
871         'description' => $description,
872         'edit_url' => $thisurl,
873       );
874     }
875     $thisdest = $result['featurefaildestination'];
876     if ($dest === true || $dest = $thisdest) {
877       $destlist[] = array(
878         'dest' => $thisdest,
879         'description' => $description,
880         'edit_url' => $thisurl,
881       );
882     }
883   }
884   return $destlist;
885 }
886 ?>
Note: See TracBrowser for help on using the browser.