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