nodebb-plugin-phone-verification 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/library.js +151 -5
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/static/lib/admin.js +137 -1
- package/static/lib/main.js +218 -32
- package/templates/admin/plugins/phone-verification.tpl +58 -11
- package/templates/admin/settings/phone-verification.tpl +58 -11
package/library.js
CHANGED
|
@@ -29,6 +29,9 @@ const defaultSettings = {
|
|
|
29
29
|
voiceServerUrl: 'https://www.call2all.co.il/ym/api/RunTzintuk',
|
|
30
30
|
voiceServerApiKey: '',
|
|
31
31
|
voiceServerEnabled: false,
|
|
32
|
+
userCallEnabled: false,
|
|
33
|
+
userCallNumber: '',
|
|
34
|
+
callApiToken: '',
|
|
32
35
|
blockUnverifiedUsers: false,
|
|
33
36
|
// הוסרו הגדרות TTS שאינן רלוונטיות לצינתוק
|
|
34
37
|
};
|
|
@@ -51,6 +54,20 @@ plugin.hashCode = function (code) {
|
|
|
51
54
|
return crypto.createHash('sha256').update(code).digest('hex');
|
|
52
55
|
};
|
|
53
56
|
|
|
57
|
+
plugin.generateApiToken = function () {
|
|
58
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
59
|
+
const bytes = crypto.randomBytes(16);
|
|
60
|
+
let token = '';
|
|
61
|
+
for (let i = 0; i < 16; i++) {
|
|
62
|
+
token += chars[bytes[i] % chars.length];
|
|
63
|
+
}
|
|
64
|
+
return token;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
plugin.generateNumericCode = function () {
|
|
68
|
+
return String(Math.floor(1000 + Math.random() * 9000));
|
|
69
|
+
};
|
|
70
|
+
|
|
54
71
|
// ==================== בדיקת הרשאות ====================
|
|
55
72
|
|
|
56
73
|
plugin.checkPostingPermissions = async function (data) {
|
|
@@ -189,7 +206,7 @@ plugin.sendTzintuk = async function (phone) {
|
|
|
189
206
|
|
|
190
207
|
// ==================== ניהול נתונים (Redis) ====================
|
|
191
208
|
|
|
192
|
-
plugin.saveVerificationCode = async function (phone, code) {
|
|
209
|
+
plugin.saveVerificationCode = async function (phone, code, options = {}) {
|
|
193
210
|
const normalizedPhone = plugin.normalizePhone(phone);
|
|
194
211
|
const now = Date.now();
|
|
195
212
|
const expiresAt = now + (CODE_EXPIRY_MINUTES * 60 * 1000);
|
|
@@ -203,6 +220,9 @@ plugin.saveVerificationCode = async function (phone, code) {
|
|
|
203
220
|
}
|
|
204
221
|
|
|
205
222
|
const data = { hashedCode: plugin.hashCode(code), attempts: 0, createdAt: now, expiresAt: expiresAt, blockedUntil: 0 };
|
|
223
|
+
if (options.storePlain === true) {
|
|
224
|
+
data.plainCode = String(code);
|
|
225
|
+
}
|
|
206
226
|
await db.setObject(key, data);
|
|
207
227
|
await db.pexpireAt(key, now + (20 * 60 * 1000));
|
|
208
228
|
return { success: true, expiresAt };
|
|
@@ -277,6 +297,23 @@ plugin.clearVerifiedPhone = async function (phone) {
|
|
|
277
297
|
if (db) await db.delete(`phone-verification:verified:${normalizedPhone}`);
|
|
278
298
|
};
|
|
279
299
|
|
|
300
|
+
plugin.getPendingPlainCode = async function (phone) {
|
|
301
|
+
const normalizedPhone = plugin.normalizePhone(phone);
|
|
302
|
+
const now = Date.now();
|
|
303
|
+
const key = `${REDIS_PREFIX}${normalizedPhone}`;
|
|
304
|
+
if (!db) return { success: false, error: 'DB_ERROR' };
|
|
305
|
+
const data = await db.getObject(key);
|
|
306
|
+
if (!data) return { success: false, error: 'CODE_NOT_FOUND' };
|
|
307
|
+
if (data.blockedUntil && parseInt(data.blockedUntil, 10) > now) {
|
|
308
|
+
return { success: false, error: 'PHONE_BLOCKED' };
|
|
309
|
+
}
|
|
310
|
+
if (parseInt(data.expiresAt, 10) < now) {
|
|
311
|
+
return { success: false, error: 'CODE_EXPIRED' };
|
|
312
|
+
}
|
|
313
|
+
if (!data.plainCode) return { success: false, error: 'CODE_UNAVAILABLE' };
|
|
314
|
+
return { success: true, code: String(data.plainCode) };
|
|
315
|
+
};
|
|
316
|
+
|
|
280
317
|
// ==================== DB Users Logic ====================
|
|
281
318
|
plugin.savePhoneToUser = async function (uid, phone, verified = true, forceOverride = false) {
|
|
282
319
|
if (!db || !User) return { success: false };
|
|
@@ -368,13 +405,19 @@ plugin.checkRegistration = async function (data) {
|
|
|
368
405
|
try {
|
|
369
406
|
const phoneNumber = data.req.body.phoneNumber;
|
|
370
407
|
const req = data.req;
|
|
371
|
-
const res = data.res;
|
|
408
|
+
// const res = data.res;
|
|
372
409
|
|
|
373
410
|
if (!phoneNumber) {
|
|
374
411
|
throw new Error('חובה להזין מספר טלפון');
|
|
375
412
|
}
|
|
376
413
|
|
|
377
414
|
const normalizedPhone = plugin.normalizePhone(phoneNumber);
|
|
415
|
+
|
|
416
|
+
const isVerified = await plugin.isPhoneVerified(normalizedPhone);
|
|
417
|
+
if (!isVerified) {
|
|
418
|
+
throw new Error('מספר הטלפון לא אומת. יש ללחוץ על "שלח לאימות" ולהזין את הקוד לפני סיום ההרשמה.');
|
|
419
|
+
}
|
|
420
|
+
|
|
378
421
|
const existingUid = await plugin.findUserByPhone(normalizedPhone);
|
|
379
422
|
|
|
380
423
|
if (existingUid) {
|
|
@@ -505,6 +548,9 @@ plugin.init = async function (params) {
|
|
|
505
548
|
router.post('/api/phone-verification/verify-code', middleware.applyCSRF, plugin.apiVerifyCode);
|
|
506
549
|
router.post('/api/phone-verification/initiate-call', middleware.applyCSRF, plugin.apiInitiateCall);
|
|
507
550
|
router.post('/api/phone-verification/check-status', middleware.applyCSRF, plugin.apiCheckStatus);
|
|
551
|
+
router.post('/api/phone-verification/request-user-call', middleware.applyCSRF, plugin.apiRequestUserCall);
|
|
552
|
+
router.get('/api/phone-verification/inbound-call', plugin.apiInboundCall);
|
|
553
|
+
router.get('/api/phone-verification/public-settings', plugin.apiGetPublicSettings);
|
|
508
554
|
|
|
509
555
|
// User Profile APIs
|
|
510
556
|
router.get('/api/user/:userslug/phone', middleware.authenticateRequest, plugin.apiGetUserPhoneProfile);
|
|
@@ -521,6 +567,7 @@ plugin.init = async function (params) {
|
|
|
521
567
|
router.get('/api/admin/plugins/phone-verification/settings', middleware.admin.checkPrivileges, plugin.apiAdminGetSettings);
|
|
522
568
|
router.post('/api/admin/plugins/phone-verification/settings', middleware.admin.checkPrivileges, middleware.applyCSRF, plugin.apiAdminSaveSettings);
|
|
523
569
|
router.post('/api/admin/plugins/phone-verification/test-call', middleware.admin.checkPrivileges, middleware.applyCSRF, plugin.apiAdminTestCall);
|
|
570
|
+
router.post('/api/admin/plugins/phone-verification/refresh-token', middleware.admin.checkPrivileges, middleware.applyCSRF, plugin.apiAdminRefreshToken);
|
|
524
571
|
};
|
|
525
572
|
plugin.apiCheckStatus = async function (req, res) {
|
|
526
573
|
try {
|
|
@@ -534,10 +581,19 @@ plugin.getSettings = async function () {
|
|
|
534
581
|
|
|
535
582
|
const isTrue = (val) => val === true || val === 'true' || val === 'on' || val === '1';
|
|
536
583
|
|
|
584
|
+
if (!settings.callApiToken) {
|
|
585
|
+
const newToken = plugin.generateApiToken();
|
|
586
|
+
await meta.settings.set('phone-verification', { ...settings, callApiToken: newToken });
|
|
587
|
+
settings.callApiToken = newToken;
|
|
588
|
+
}
|
|
589
|
+
|
|
537
590
|
return {
|
|
538
591
|
voiceServerUrl: settings.voiceServerUrl || defaultSettings.voiceServerUrl,
|
|
539
592
|
voiceServerApiKey: settings.voiceServerApiKey || '',
|
|
540
593
|
voiceServerEnabled: isTrue(settings.voiceServerEnabled),
|
|
594
|
+
userCallEnabled: isTrue(settings.userCallEnabled),
|
|
595
|
+
userCallNumber: settings.userCallNumber || '',
|
|
596
|
+
callApiToken: settings.callApiToken || '',
|
|
541
597
|
blockUnverifiedUsers: isTrue(settings.blockUnverifiedUsers)
|
|
542
598
|
};
|
|
543
599
|
};
|
|
@@ -548,6 +604,9 @@ plugin.saveSettings = async function (settings) {
|
|
|
548
604
|
voiceServerUrl: settings.voiceServerUrl || defaultSettings.voiceServerUrl,
|
|
549
605
|
voiceServerApiKey: settings.voiceServerApiKey || '',
|
|
550
606
|
voiceServerEnabled: settings.voiceServerEnabled ? 'true' : 'false',
|
|
607
|
+
userCallEnabled: settings.userCallEnabled ? 'true' : 'false',
|
|
608
|
+
userCallNumber: settings.userCallNumber || '',
|
|
609
|
+
callApiToken: settings.callApiToken || '',
|
|
551
610
|
blockUnverifiedUsers: settings.blockUnverifiedUsers ? 'true' : 'false'
|
|
552
611
|
});
|
|
553
612
|
return true;
|
|
@@ -564,14 +623,24 @@ plugin.apiAdminGetSettings = async function (req, res) {
|
|
|
564
623
|
|
|
565
624
|
plugin.apiAdminSaveSettings = async function (req, res) {
|
|
566
625
|
try {
|
|
567
|
-
const { voiceServerApiKey, ...rest } = req.body;
|
|
626
|
+
const { voiceServerApiKey, callApiToken, ...rest } = req.body;
|
|
568
627
|
const current = await plugin.getSettings();
|
|
569
628
|
const apiKey = voiceServerApiKey === '********' ? current.voiceServerApiKey : voiceServerApiKey;
|
|
570
|
-
|
|
629
|
+
const tokenToSave = callApiToken || current.callApiToken;
|
|
630
|
+
await plugin.saveSettings({ ...rest, voiceServerApiKey: apiKey, callApiToken: tokenToSave });
|
|
571
631
|
res.json({ success: true });
|
|
572
632
|
} catch (err) { res.json({ success: false }); }
|
|
573
633
|
};
|
|
574
634
|
|
|
635
|
+
plugin.apiAdminRefreshToken = async function (req, res) {
|
|
636
|
+
try {
|
|
637
|
+
const current = await plugin.getSettings();
|
|
638
|
+
const newToken = plugin.generateApiToken();
|
|
639
|
+
await plugin.saveSettings({ ...current, callApiToken: newToken });
|
|
640
|
+
res.json({ success: true, token: newToken });
|
|
641
|
+
} catch (err) { res.json({ success: false }); }
|
|
642
|
+
};
|
|
643
|
+
|
|
575
644
|
plugin.apiAdminTestCall = async function (req, res) {
|
|
576
645
|
try {
|
|
577
646
|
const { phoneNumber } = req.body;
|
|
@@ -618,6 +687,40 @@ plugin.apiSendCode = async function (req, res) {
|
|
|
618
687
|
}
|
|
619
688
|
};
|
|
620
689
|
|
|
690
|
+
plugin.apiRequestUserCall = async function (req, res) {
|
|
691
|
+
try {
|
|
692
|
+
const { phoneNumber } = req.body;
|
|
693
|
+
if (!phoneNumber) return res.json({ success: false, error: 'MISSING' });
|
|
694
|
+
|
|
695
|
+
const settings = await plugin.getSettings();
|
|
696
|
+
if (!settings.userCallEnabled) {
|
|
697
|
+
return res.json({ success: false, error: 'CALL_DISABLED', message: 'אימות בשיחה יזומה אינו פעיל' });
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
const clientIp = req.ip || req.headers['x-forwarded-for'];
|
|
701
|
+
const ipCheck = await plugin.checkIpRateLimit(clientIp);
|
|
702
|
+
if (!ipCheck.allowed) return res.json(ipCheck);
|
|
703
|
+
await plugin.incrementIpCounter(clientIp);
|
|
704
|
+
|
|
705
|
+
const clean = plugin.normalizePhone(phoneNumber.replace(/\D/g, ''));
|
|
706
|
+
if (!plugin.validatePhoneNumber(clean)) return res.json({ success: false, error: 'INVALID' });
|
|
707
|
+
|
|
708
|
+
const existingUid = await plugin.findUserByPhone(clean);
|
|
709
|
+
if (existingUid && (!req.uid || parseInt(existingUid) !== parseInt(req.uid))) {
|
|
710
|
+
return res.json({ success: false, error: 'EXISTS', message: 'המספר תפוס' });
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
const code = plugin.generateNumericCode();
|
|
714
|
+
const saveResult = await plugin.saveVerificationCode(clean, code, { storePlain: true });
|
|
715
|
+
if (!saveResult.success) return res.json(saveResult);
|
|
716
|
+
|
|
717
|
+
res.json({ success: true, message: 'הקוד הוכן. נא להתקשר לקו לצורך שמיעת הקוד.', callNumber: settings.userCallNumber || '' });
|
|
718
|
+
} catch (err) {
|
|
719
|
+
console.error(err);
|
|
720
|
+
res.json({ success: false });
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
|
|
621
724
|
plugin.apiVerifyCode = async function (req, res) {
|
|
622
725
|
try {
|
|
623
726
|
const { phoneNumber, code } = req.body;
|
|
@@ -671,6 +774,49 @@ plugin.apiInitiateCall = async function (req, res) {
|
|
|
671
774
|
}
|
|
672
775
|
};
|
|
673
776
|
|
|
777
|
+
plugin.apiInboundCall = async function (req, res) {
|
|
778
|
+
try {
|
|
779
|
+
const settings = await plugin.getSettings();
|
|
780
|
+
const token = req.query.token;
|
|
781
|
+
const phone = req.query.ApiPhone;
|
|
782
|
+
|
|
783
|
+
if (!token || token !== settings.callApiToken) {
|
|
784
|
+
return res.status(403).send('forbidden');
|
|
785
|
+
}
|
|
786
|
+
if (!phone || !plugin.validatePhoneNumber(phone)) {
|
|
787
|
+
return res.status(400).send('invalid');
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
const pending = await plugin.getPendingPlainCode(phone);
|
|
791
|
+
if (!pending.success) {
|
|
792
|
+
return res.status(404).send('not found');
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
const code = pending.code;
|
|
796
|
+
const payload = `read=t-הקוד שלכם החד פעמי הוא.d-${code}.t-לשמיעה חוזרת הַקִּישׁוּ1=MOP,,1,1,15,NO,,,,1,3,OK,,,no`;
|
|
797
|
+
res.set('Content-Type', 'text/plain; charset=utf-8');
|
|
798
|
+
res.send(payload);
|
|
799
|
+
} catch (err) {
|
|
800
|
+
res.status(500).send('error');
|
|
801
|
+
}
|
|
802
|
+
};
|
|
803
|
+
|
|
804
|
+
plugin.apiGetPublicSettings = async function (req, res) {
|
|
805
|
+
try {
|
|
806
|
+
const settings = await plugin.getSettings();
|
|
807
|
+
res.json({
|
|
808
|
+
success: true,
|
|
809
|
+
settings: {
|
|
810
|
+
voiceServerEnabled: settings.voiceServerEnabled,
|
|
811
|
+
userCallEnabled: settings.userCallEnabled,
|
|
812
|
+
userCallNumber: settings.userCallNumber || ''
|
|
813
|
+
}
|
|
814
|
+
});
|
|
815
|
+
} catch (e) {
|
|
816
|
+
res.json({ success: false });
|
|
817
|
+
}
|
|
818
|
+
};
|
|
819
|
+
|
|
674
820
|
plugin.apiGetUserPhoneProfile = async function (req, res) {
|
|
675
821
|
try {
|
|
676
822
|
const uid = await User.getUidByUserslug(req.params.userslug);
|
|
@@ -769,4 +915,4 @@ plugin.userDelete = async function (data) {
|
|
|
769
915
|
} catch (e) {}
|
|
770
916
|
};
|
|
771
917
|
|
|
772
|
-
module.exports = plugin;
|
|
918
|
+
module.exports = plugin;
|
package/package.json
CHANGED
package/plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "nodebb-plugin-phone-verification",
|
|
3
3
|
"name": "Phone Verification",
|
|
4
4
|
"description": "אימות מספר טלפון נייד בתהליך ההרשמה לפורום ובפרופיל המשתמש",
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "3.0.0",
|
|
6
6
|
"library": "./library.js",
|
|
7
7
|
"hooks": [
|
|
8
8
|
{ "hook": "filter:register.check", "method": "checkRegistration" },
|
package/static/lib/admin.js
CHANGED
|
@@ -11,6 +11,76 @@ define('admin/plugins/phone-verification', ['settings', 'bootbox', 'alerts'], fu
|
|
|
11
11
|
|
|
12
12
|
Settings.load('phone-verification', $('#voice-settings-form'));
|
|
13
13
|
|
|
14
|
+
function buildApiLink() {
|
|
15
|
+
var base = window.location.origin || '';
|
|
16
|
+
return base + config.relative_path + '/api/phone-verification/inbound-call';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function buildUserCallConfig(token) {
|
|
20
|
+
var apiLink = buildApiLink();
|
|
21
|
+
return [
|
|
22
|
+
'type=api',
|
|
23
|
+
'api_link=' + apiLink,
|
|
24
|
+
'api_hangup_send=no',
|
|
25
|
+
'api_add_0=token=' + token
|
|
26
|
+
].join('\n');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function showUserCallSetupModal(token, onConfirm) {
|
|
30
|
+
var configText = buildUserCallConfig(token);
|
|
31
|
+
var modalHtml =
|
|
32
|
+
'<div>' +
|
|
33
|
+
'<p>לשם ביצוע הפעולה יש להגדיר בקו שלכם בהגדרות השלוחה הראשית או השלוחה הרצויה לאימות את ההגדרות הבאות:</p>' +
|
|
34
|
+
'<div class="mb-2">' +
|
|
35
|
+
'<button type="button" class="btn btn-default btn-sm" id="copy-user-call-config">העתק</button>' +
|
|
36
|
+
'</div>' +
|
|
37
|
+
'<pre style="white-space: pre-wrap;"><code id="user-call-config">' + configText + '</code></pre>' +
|
|
38
|
+
'</div>';
|
|
39
|
+
|
|
40
|
+
var dialog = bootbox.dialog({
|
|
41
|
+
title: 'הגדרת שיחה יזומה',
|
|
42
|
+
message: modalHtml,
|
|
43
|
+
buttons: {
|
|
44
|
+
cancel: {
|
|
45
|
+
label: 'ביטול',
|
|
46
|
+
className: 'btn-ghost',
|
|
47
|
+
callback: function() {
|
|
48
|
+
if (typeof onConfirm === 'function') onConfirm(false);
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
ok: {
|
|
52
|
+
label: 'הגדרתי את הקו שיעביר להמשך',
|
|
53
|
+
className: 'btn-primary',
|
|
54
|
+
callback: function() {
|
|
55
|
+
if (typeof onConfirm === 'function') onConfirm(true);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
dialog.on('shown.bs.modal', function() {
|
|
62
|
+
$('#copy-user-call-config').off('click').on('click', function() {
|
|
63
|
+
var text = $('#user-call-config').text();
|
|
64
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
65
|
+
navigator.clipboard.writeText(text).then(function() {
|
|
66
|
+
alerts.success('הועתק ללוח');
|
|
67
|
+
}).catch(function() {
|
|
68
|
+
alerts.error('לא ניתן להעתיק');
|
|
69
|
+
});
|
|
70
|
+
} else {
|
|
71
|
+
var $temp = $('<textarea>').val(text).appendTo('body').select();
|
|
72
|
+
try {
|
|
73
|
+
document.execCommand('copy');
|
|
74
|
+
alerts.success('הועתק ללוח');
|
|
75
|
+
} catch (e) {
|
|
76
|
+
alerts.error('לא ניתן להעתיק');
|
|
77
|
+
}
|
|
78
|
+
$temp.remove();
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
14
84
|
$('#save-settings-btn').on('click', function(e) {
|
|
15
85
|
e.preventDefault();
|
|
16
86
|
Settings.save('phone-verification', $('#voice-settings-form'), function() {
|
|
@@ -18,6 +88,53 @@ define('admin/plugins/phone-verification', ['settings', 'bootbox', 'alerts'], fu
|
|
|
18
88
|
});
|
|
19
89
|
});
|
|
20
90
|
|
|
91
|
+
$('#userCallEnabled').on('change', function() {
|
|
92
|
+
var $checkbox = $(this);
|
|
93
|
+
if (!$checkbox.is(':checked')) return;
|
|
94
|
+
|
|
95
|
+
var token = $('#callApiToken').val();
|
|
96
|
+
if (!token) {
|
|
97
|
+
$.post(config.relative_path + '/api/admin/plugins/phone-verification/refresh-token', { _csrf: config.csrf_token }, function(res) {
|
|
98
|
+
if (res && res.success && res.token) {
|
|
99
|
+
$('#callApiToken').val(res.token);
|
|
100
|
+
showUserCallSetupModal(res.token, function(confirmed) {
|
|
101
|
+
if (!confirmed) {
|
|
102
|
+
$checkbox.prop('checked', false);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
} else {
|
|
106
|
+
alerts.error('שגיאה ביצירת טוקן');
|
|
107
|
+
$checkbox.prop('checked', false);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
showUserCallSetupModal(token, function(confirmed) {
|
|
114
|
+
if (!confirmed) {
|
|
115
|
+
$checkbox.prop('checked', false);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
$('#refresh-call-token-btn').on('click', function() {
|
|
121
|
+
bootbox.confirm({
|
|
122
|
+
title: 'רענון טוקן',
|
|
123
|
+
message: 'אחרי הרענון יש לעדכן את הגדרות השלוחה בקו. האם להמשיך?',
|
|
124
|
+
callback: function(result) {
|
|
125
|
+
if (!result) return;
|
|
126
|
+
$.post(config.relative_path + '/api/admin/plugins/phone-verification/refresh-token', { _csrf: config.csrf_token }, function(res) {
|
|
127
|
+
if (res && res.success && res.token) {
|
|
128
|
+
$('#callApiToken').val(res.token);
|
|
129
|
+
alerts.success('הטוקן עודכן');
|
|
130
|
+
} else {
|
|
131
|
+
alerts.error('שגיאה ברענון הטוקן');
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
21
138
|
|
|
22
139
|
function renderPagination(curr, total) {
|
|
23
140
|
currentPage = curr;
|
|
@@ -133,6 +250,25 @@ define('admin/plugins/phone-verification', ['settings', 'bootbox', 'alerts'], fu
|
|
|
133
250
|
});
|
|
134
251
|
});
|
|
135
252
|
|
|
253
|
+
$('#test-user-call-btn').on('click', function() {
|
|
254
|
+
var phone = $('#test-user-call-phone').val();
|
|
255
|
+
if(!phone) return alerts.error('נא להזין מספר לבדיקה');
|
|
256
|
+
|
|
257
|
+
$.post(config.relative_path + '/api/admin/plugins/phone-verification/test-user-call', { phoneNumber: phone, _csrf: config.csrf_token }, function(res) {
|
|
258
|
+
if(res.success) {
|
|
259
|
+
var msg = 'קוד אימות נוצר בהצלחה';
|
|
260
|
+
if (res.code) {
|
|
261
|
+
msg += '<br><strong>קוד האימות: ' + res.code + '</strong>';
|
|
262
|
+
}
|
|
263
|
+
if (res.phoneNumber) {
|
|
264
|
+
msg += '<br><strong>מספר הקו: ' + res.phoneNumber + '</strong>';
|
|
265
|
+
}
|
|
266
|
+
alerts.success(msg);
|
|
267
|
+
}
|
|
268
|
+
else alerts.error(res.message || 'שגיאה ביצירת קוד האימות');
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
136
272
|
$('#users-table').on('click', '.verify-user-btn', function() {
|
|
137
273
|
var uid = $(this).data('uid');
|
|
138
274
|
var name = $(this).data('name');
|
|
@@ -181,4 +317,4 @@ define('admin/plugins/phone-verification', ['settings', 'bootbox', 'alerts'], fu
|
|
|
181
317
|
};
|
|
182
318
|
|
|
183
319
|
return ACP;
|
|
184
|
-
});
|
|
320
|
+
});
|
package/static/lib/main.js
CHANGED
|
@@ -8,12 +8,32 @@ define('forum/phone-verification', ['hooks', 'translator'], function (hooks, tra
|
|
|
8
8
|
return /^05\d{8}$/.test(cleanPhone);
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
let publicSettingsCache = null;
|
|
12
|
+
|
|
13
|
+
function loadPublicSettings() {
|
|
14
|
+
if (publicSettingsCache) return $.Deferred().resolve(publicSettingsCache).promise();
|
|
15
|
+
return $.getJSON(config.relative_path + '/api/phone-verification/public-settings')
|
|
16
|
+
.then(function(res) {
|
|
17
|
+
if (res && res.success && res.settings) {
|
|
18
|
+
publicSettingsCache = res.settings;
|
|
19
|
+
} else {
|
|
20
|
+
publicSettingsCache = { voiceServerEnabled: true, userCallEnabled: false, userCallNumber: '' };
|
|
21
|
+
}
|
|
22
|
+
return publicSettingsCache;
|
|
23
|
+
})
|
|
24
|
+
.catch(function() {
|
|
25
|
+
publicSettingsCache = { voiceServerEnabled: true, userCallEnabled: false, userCallNumber: '' };
|
|
26
|
+
return publicSettingsCache;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
11
30
|
// ==================== לוגיקה לעמוד הרשמה (Registration) ====================
|
|
12
31
|
|
|
13
32
|
const Registration = {
|
|
14
33
|
resendTimer: null,
|
|
15
34
|
resendCountdown: 0,
|
|
16
35
|
phoneVerified: false,
|
|
36
|
+
registerBtn: null,
|
|
17
37
|
|
|
18
38
|
validatePhone: function(phone) {
|
|
19
39
|
return isValidIsraeliPhone(phone);
|
|
@@ -55,7 +75,9 @@ define('forum/phone-verification', ['hooks', 'translator'], function (hooks, tra
|
|
|
55
75
|
if (this.resendCountdown > 0) {
|
|
56
76
|
$btn.prop('disabled', true).text('שלח שוב (' + this.resendCountdown + ')');
|
|
57
77
|
} else {
|
|
58
|
-
|
|
78
|
+
const method = this.getSelectedMethod();
|
|
79
|
+
const label = method === 'user-call' ? 'הכן קוד שוב' : 'שלח צינתוק שוב';
|
|
80
|
+
$btn.prop('disabled', false).text(label);
|
|
59
81
|
}
|
|
60
82
|
},
|
|
61
83
|
|
|
@@ -66,9 +88,50 @@ define('forum/phone-verification', ['hooks', 'translator'], function (hooks, tra
|
|
|
66
88
|
if ($('#phoneNumber').length) return;
|
|
67
89
|
|
|
68
90
|
self.phoneVerified = false;
|
|
91
|
+
const $registerBtn = $form.find('button[type="submit"]');
|
|
92
|
+
self.registerBtn = $registerBtn;
|
|
93
|
+
if ($registerBtn.length) {
|
|
94
|
+
$registerBtn.prop('disabled', true);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
loadPublicSettings().then(function(settings) {
|
|
98
|
+
const phoneHtml = self.buildPhoneHtml(settings);
|
|
99
|
+
if ($registerBtn.length) {
|
|
100
|
+
$(phoneHtml).insertBefore($registerBtn);
|
|
101
|
+
} else {
|
|
102
|
+
$form.append(phoneHtml);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
self.attachEventListeners(settings);
|
|
106
|
+
self.checkExistingVerification();
|
|
107
|
+
});
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
buildPhoneHtml: function(settings) {
|
|
111
|
+
const showTzintuk = settings.voiceServerEnabled;
|
|
112
|
+
const showUserCall = settings.userCallEnabled;
|
|
113
|
+
const userCallNumber = settings.userCallNumber || '';
|
|
114
|
+
const methodsHtml = `
|
|
115
|
+
<div class="mb-2 d-flex flex-column gap-2 hidden" id="verification-methods">
|
|
116
|
+
<label class="form-label fw-bold">בחר שיטת אימות</label>
|
|
117
|
+
<div class="d-flex flex-column gap-1">
|
|
118
|
+
${showTzintuk ? `
|
|
119
|
+
<label class="form-check-label">
|
|
120
|
+
<input class="form-check-input" type="radio" name="verificationMethod" value="tzintuk" />
|
|
121
|
+
אני רוצה לקבל את הקוד בצינתוק
|
|
122
|
+
</label>` : ''}
|
|
123
|
+
${showUserCall ? `
|
|
124
|
+
<label class="form-check-label">
|
|
125
|
+
<input class="form-check-input" type="radio" name="verificationMethod" value="user-call" />
|
|
126
|
+
אני רוצה להתקשר לקו ולשמוע את הקוד
|
|
127
|
+
</label>` : ''}
|
|
128
|
+
</div>
|
|
129
|
+
<div class="form-text text-xs" id="method-help"></div>
|
|
130
|
+
<div class="form-text text-xs" id="user-call-number-text" ${userCallNumber ? '' : 'style="display:none;"'}>מספר הקו: <span dir="ltr">${userCallNumber}</span></div>
|
|
131
|
+
</div>
|
|
132
|
+
`;
|
|
69
133
|
|
|
70
|
-
|
|
71
|
-
const phoneHtml = `
|
|
134
|
+
return `
|
|
72
135
|
<div class="mb-2 d-flex flex-column gap-2" id="phone-verification-container">
|
|
73
136
|
<label for="phoneNumber">מספר טלפון <span class="text-danger">*</span></label>
|
|
74
137
|
<div class="d-flex flex-column">
|
|
@@ -79,7 +142,7 @@ define('forum/phone-verification', ['hooks', 'translator'], function (hooks, tra
|
|
|
79
142
|
<i class="fa fa-phone"></i> שלח לאימות
|
|
80
143
|
</button>
|
|
81
144
|
</div>
|
|
82
|
-
|
|
145
|
+
${methodsHtml}
|
|
83
146
|
<div id="phone-error" class="text-danger text-xs hidden"></div>
|
|
84
147
|
<div id="phone-success" class="text-success text-xs hidden"></div>
|
|
85
148
|
</div>
|
|
@@ -105,28 +168,89 @@ define('forum/phone-verification', ['hooks', 'translator'], function (hooks, tra
|
|
|
105
168
|
<i class="fa fa-check-circle"></i> מספר הטלפון אומת בהצלחה!
|
|
106
169
|
</div>
|
|
107
170
|
`;
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
getSelectedMethod: function() {
|
|
174
|
+
const selected = $('input[name="verificationMethod"]:checked').val();
|
|
175
|
+
return selected || 'tzintuk';
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
updateMethodHelp: function(settings) {
|
|
179
|
+
const method = this.getSelectedMethod();
|
|
180
|
+
if (method === 'user-call') {
|
|
181
|
+
$('#method-help').text('לאחר לחיצה על שלח לאימות יש להתקשר לקו המוצג ולשמוע את קוד האימות.');
|
|
182
|
+
$('#verificationCode').attr('placeholder', 'קוד שהושמע בשיחה');
|
|
183
|
+
$('#user-call-number-text').toggle(!!(settings.userCallNumber && settings.userCallNumber.length));
|
|
112
184
|
} else {
|
|
113
|
-
$
|
|
185
|
+
$('#method-help').text('תקבל שיחה (צינתוק). קוד האימות הוא 4 הספרות האחרונות של המספר המתקשר.');
|
|
186
|
+
$('#verificationCode').attr('placeholder', '4 ספרות אחרונות של המספר המחייג');
|
|
187
|
+
$('#user-call-number-text').hide();
|
|
114
188
|
}
|
|
115
|
-
|
|
116
|
-
self.attachEventListeners();
|
|
117
|
-
self.checkExistingVerification();
|
|
118
189
|
},
|
|
119
190
|
|
|
120
|
-
attachEventListeners: function() {
|
|
191
|
+
attachEventListeners: function(settings) {
|
|
121
192
|
const self = this;
|
|
122
193
|
|
|
123
|
-
|
|
194
|
+
function showMethodsIfValid() {
|
|
195
|
+
const phone = $('#phoneNumber').val().trim();
|
|
196
|
+
if (self.validatePhone(phone)) {
|
|
197
|
+
$('#verification-methods').removeClass('hidden');
|
|
198
|
+
if ($('input[name="verificationMethod"]:checked').length === 0) {
|
|
199
|
+
if (settings.voiceServerEnabled) {
|
|
200
|
+
$('input[name="verificationMethod"][value="tzintuk"]').prop('checked', true);
|
|
201
|
+
} else if (settings.userCallEnabled) {
|
|
202
|
+
$('input[name="verificationMethod"][value="user-call"]').prop('checked', true);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
self.updateMethodHelp(settings);
|
|
206
|
+
} else {
|
|
207
|
+
$('#verification-methods').addClass('hidden');
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
$('#phoneNumber').on('input blur', function() {
|
|
212
|
+
showMethodsIfValid();
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
$('body').on('change', 'input[name="verificationMethod"]', function() {
|
|
216
|
+
self.updateMethodHelp(settings);
|
|
217
|
+
self.updateResendButton();
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
function requestVerification(method) {
|
|
124
221
|
const phone = $('#phoneNumber').val().trim();
|
|
125
222
|
self.hideMessages();
|
|
126
|
-
|
|
127
|
-
const $btn = $(
|
|
223
|
+
|
|
224
|
+
const $btn = $('#send-code-btn');
|
|
128
225
|
$btn.prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i> שולח...');
|
|
129
|
-
|
|
226
|
+
|
|
227
|
+
if (method === 'user-call') {
|
|
228
|
+
$.ajax({
|
|
229
|
+
url: config.relative_path + '/api/phone-verification/request-user-call',
|
|
230
|
+
method: 'POST',
|
|
231
|
+
data: { phoneNumber: phone },
|
|
232
|
+
headers: { 'x-csrf-token': config.csrf_token },
|
|
233
|
+
success: function (response) {
|
|
234
|
+
if (response.success) {
|
|
235
|
+
var numberText = response.callNumber ? (' מספר הקו: ' + response.callNumber) : '';
|
|
236
|
+
self.showSuccess('הקוד הוכן. נא להתקשר לקו לשמיעת הקוד.' + numberText);
|
|
237
|
+
$('#verification-code-container').removeClass('hidden');
|
|
238
|
+
$('#phoneNumber').prop('readonly', true);
|
|
239
|
+
$btn.addClass('hidden');
|
|
240
|
+
self.startResendTimer();
|
|
241
|
+
} else {
|
|
242
|
+
self.showError(response.message || 'שגיאה בשליחה');
|
|
243
|
+
$btn.prop('disabled', false).html('<i class="fa fa-phone"></i> שלח לאימות');
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
error: function() {
|
|
247
|
+
self.showError('שגיאת תקשורת');
|
|
248
|
+
$btn.prop('disabled', false).html('<i class="fa fa-phone"></i> שלח לאימות');
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
130
254
|
$.ajax({
|
|
131
255
|
url: config.relative_path + '/api/phone-verification/send-code',
|
|
132
256
|
method: 'POST',
|
|
@@ -149,6 +273,11 @@ define('forum/phone-verification', ['hooks', 'translator'], function (hooks, tra
|
|
|
149
273
|
$btn.prop('disabled', false).html('<i class="fa fa-phone"></i> שלח לאימות');
|
|
150
274
|
}
|
|
151
275
|
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
$('#send-code-btn').off('click').on('click', function () {
|
|
279
|
+
const method = self.getSelectedMethod();
|
|
280
|
+
requestVerification(method);
|
|
152
281
|
});
|
|
153
282
|
|
|
154
283
|
$('#verify-code-btn').off('click').on('click', function () {
|
|
@@ -178,6 +307,9 @@ define('forum/phone-verification', ['hooks', 'translator'], function (hooks, tra
|
|
|
178
307
|
} else {
|
|
179
308
|
$('#phoneNumberVerified').val(phone);
|
|
180
309
|
}
|
|
310
|
+
if (self.registerBtn && self.registerBtn.length) {
|
|
311
|
+
self.registerBtn.prop('disabled', false);
|
|
312
|
+
}
|
|
181
313
|
} else {
|
|
182
314
|
self.showError(response.message || 'קוד שגוי');
|
|
183
315
|
}
|
|
@@ -185,6 +317,13 @@ define('forum/phone-verification', ['hooks', 'translator'], function (hooks, tra
|
|
|
185
317
|
});
|
|
186
318
|
});
|
|
187
319
|
|
|
320
|
+
$('#resend-code-btn').off('click').on('click', function () {
|
|
321
|
+
if (self.resendCountdown > 0) return;
|
|
322
|
+
const method = self.getSelectedMethod();
|
|
323
|
+
$('#send-code-btn').removeClass('hidden');
|
|
324
|
+
requestVerification(method);
|
|
325
|
+
});
|
|
326
|
+
|
|
188
327
|
$('[component="register/local"]').off('submit.phone').on('submit.phone', function (e) {
|
|
189
328
|
if (!self.phoneVerified) {
|
|
190
329
|
e.preventDefault();
|
|
@@ -259,7 +398,7 @@ define('forum/phone-verification', ['hooks', 'translator'], function (hooks, tra
|
|
|
259
398
|
<i class="fa fa-info-circle"></i>
|
|
260
399
|
${isVerified
|
|
261
400
|
? 'המספר הנוכחי מאומת. שינוי המספר יחייב אימות מחדש.'
|
|
262
|
-
: 'יש להזין מספר ולקבל
|
|
401
|
+
: 'יש להזין מספר ולקבל אימות.'}
|
|
263
402
|
</div>
|
|
264
403
|
</div>
|
|
265
404
|
<div id="modal-alert-area"></div>
|
|
@@ -275,7 +414,7 @@ define('forum/phone-verification', ['hooks', 'translator'], function (hooks, tra
|
|
|
275
414
|
className: 'btn-ghost'
|
|
276
415
|
},
|
|
277
416
|
verify: {
|
|
278
|
-
label: '
|
|
417
|
+
label: 'המשך לאימות',
|
|
279
418
|
className: 'btn-primary',
|
|
280
419
|
callback: function() {
|
|
281
420
|
const newPhone = $('#modal-phoneNumber').val();
|
|
@@ -308,20 +447,14 @@ define('forum/phone-verification', ['hooks', 'translator'], function (hooks, tra
|
|
|
308
447
|
}, function(res) {
|
|
309
448
|
if (!res.success) {
|
|
310
449
|
showModalAlert(res.message || res.error, 'danger');
|
|
311
|
-
$btn.prop('disabled', false).text('
|
|
450
|
+
$btn.prop('disabled', false).text('המשך לאימות');
|
|
312
451
|
return;
|
|
313
452
|
}
|
|
314
453
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
_csrf: config.csrf_token
|
|
318
|
-
}, function(callRes) {
|
|
319
|
-
if (callRes.success) {
|
|
320
|
-
dialog.modal('hide');
|
|
321
|
-
|
|
322
|
-
// עדכון ה-Prompt לקליטת 4 ספרות
|
|
454
|
+
loadPublicSettings().then(function(settings) {
|
|
455
|
+
function askForCode(title) {
|
|
323
456
|
bootbox.prompt({
|
|
324
|
-
title:
|
|
457
|
+
title: title,
|
|
325
458
|
inputType: 'number',
|
|
326
459
|
callback: function (code) {
|
|
327
460
|
if (!code) return;
|
|
@@ -339,9 +472,62 @@ define('forum/phone-verification', ['hooks', 'translator'], function (hooks, tra
|
|
|
339
472
|
});
|
|
340
473
|
}
|
|
341
474
|
});
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function sendByMethod(method) {
|
|
478
|
+
if (method === 'user-call') {
|
|
479
|
+
$.post(config.relative_path + '/api/phone-verification/request-user-call', {
|
|
480
|
+
phoneNumber: phone,
|
|
481
|
+
_csrf: config.csrf_token
|
|
482
|
+
}, function(callRes) {
|
|
483
|
+
if (callRes.success) {
|
|
484
|
+
dialog.modal('hide');
|
|
485
|
+
var title = "התקשר לקו לשמיעת קוד האימות";
|
|
486
|
+
if (callRes.callNumber) title += " (" + callRes.callNumber + ")";
|
|
487
|
+
askForCode(title);
|
|
488
|
+
} else {
|
|
489
|
+
showModalAlert(callRes.message || 'שגיאה בהכנת הקוד', 'danger');
|
|
490
|
+
$btn.prop('disabled', false).text('המשך לאימות');
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
$.post(config.relative_path + '/api/phone-verification/send-code', {
|
|
497
|
+
phoneNumber: phone,
|
|
498
|
+
_csrf: config.csrf_token
|
|
499
|
+
}, function(callRes) {
|
|
500
|
+
if (callRes.success) {
|
|
501
|
+
dialog.modal('hide');
|
|
502
|
+
askForCode("הזן את 4 הספרות האחרונות של המספר שחייג אליך כעת");
|
|
503
|
+
} else {
|
|
504
|
+
showModalAlert(callRes.message || 'שגיאה בשליחת הצינתוק', 'danger');
|
|
505
|
+
$btn.prop('disabled', false).text('המשך לאימות');
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if (settings.voiceServerEnabled && settings.userCallEnabled) {
|
|
511
|
+
bootbox.dialog({
|
|
512
|
+
title: 'בחר שיטת אימות',
|
|
513
|
+
message: 'ניתן לבחור אימות בצינתוק או שיחה יזומה מצד המשתמש.',
|
|
514
|
+
buttons: {
|
|
515
|
+
tzintuk: {
|
|
516
|
+
label: 'צינתוק',
|
|
517
|
+
className: 'btn-primary',
|
|
518
|
+
callback: function() { sendByMethod('tzintuk'); }
|
|
519
|
+
},
|
|
520
|
+
userCall: {
|
|
521
|
+
label: 'שיחה יזומה',
|
|
522
|
+
className: 'btn-success',
|
|
523
|
+
callback: function() { sendByMethod('user-call'); }
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
} else if (settings.userCallEnabled) {
|
|
528
|
+
sendByMethod('user-call');
|
|
342
529
|
} else {
|
|
343
|
-
|
|
344
|
-
$btn.prop('disabled', false).text('שלח צינתוק');
|
|
530
|
+
sendByMethod('tzintuk');
|
|
345
531
|
}
|
|
346
532
|
});
|
|
347
533
|
});
|
|
@@ -432,4 +618,4 @@ define('forum/phone-verification', ['hooks', 'translator'], function (hooks, tra
|
|
|
432
618
|
}
|
|
433
619
|
|
|
434
620
|
return Plugin;
|
|
435
|
-
});
|
|
621
|
+
});
|
|
@@ -14,10 +14,37 @@
|
|
|
14
14
|
<div class="form-group">
|
|
15
15
|
<label for="voiceServerEnabled">
|
|
16
16
|
<input type="checkbox" id="voiceServerEnabled" name="voiceServerEnabled" />
|
|
17
|
-
|
|
17
|
+
אימות ע"י צינתוק (0.1 יחידות)
|
|
18
18
|
</label>
|
|
19
19
|
<p class="help-block">כאשר מופעל, התוסף ישלח צינתוק (שיחה מנותקת) למשתמש, שיצטרך לאמת את 4 הספרות האחרונות של המספר המתקשר.</p>
|
|
20
20
|
</div>
|
|
21
|
+
|
|
22
|
+
<div class="form-group">
|
|
23
|
+
<label for="userCallEnabled">
|
|
24
|
+
<input type="checkbox" id="userCallEnabled" name="userCallEnabled" />
|
|
25
|
+
אימות ע"י שיחה יזומה מצד המשתמש (חינם)
|
|
26
|
+
</label>
|
|
27
|
+
<p class="help-block">כאשר מופעל, המשתמש יקבל קוד אימות לאחר שיתקשר לקו שלכם.</p>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div class="form-group">
|
|
31
|
+
<label for="userCallNumber">מספר הקו שיוצג למשתמשים</label>
|
|
32
|
+
<input type="text" class="form-control" id="userCallNumber" name="userCallNumber"
|
|
33
|
+
placeholder="לדוגמה: 03-1234567" dir="ltr" />
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<div class="form-group">
|
|
37
|
+
<label for="callApiToken">טוקן ייחודי לפורום</label>
|
|
38
|
+
<div class="input-group">
|
|
39
|
+
<input type="text" class="form-control" id="callApiToken" name="callApiToken" readonly dir="ltr" />
|
|
40
|
+
<span class="input-group-btn">
|
|
41
|
+
<button type="button" class="btn btn-default" id="refresh-call-token-btn">
|
|
42
|
+
<i class="fa fa-refresh"></i> רענן טוקן
|
|
43
|
+
</button>
|
|
44
|
+
</span>
|
|
45
|
+
</div>
|
|
46
|
+
<p class="help-block">טוקן זה משמש לזיהוי הקו שלכם ב־API.</p>
|
|
47
|
+
</div>
|
|
21
48
|
|
|
22
49
|
<div class="form-group">
|
|
23
50
|
<label for="voiceServerApiKey">Token של Call2All</label>
|
|
@@ -67,16 +94,36 @@
|
|
|
67
94
|
|
|
68
95
|
<hr />
|
|
69
96
|
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
<
|
|
74
|
-
|
|
97
|
+
<div class="row">
|
|
98
|
+
<div class="col-md-6">
|
|
99
|
+
<h4>בדיקת צינתוק</h4>
|
|
100
|
+
<div class="form-inline">
|
|
101
|
+
<div class="form-group">
|
|
102
|
+
<input type="text" class="form-control" id="test-phone"
|
|
103
|
+
placeholder="05X-XXXXXXX" dir="ltr" style="width: 150px;" />
|
|
104
|
+
</div>
|
|
105
|
+
<button type="button" class="btn btn-warning" id="test-call-btn">
|
|
106
|
+
<i class="fa fa-phone"></i> שלח צינתוק בדיקה
|
|
107
|
+
</button>
|
|
108
|
+
<span id="test-status" style="margin-right: 10px;"></span>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
<div class="col-md-6">
|
|
112
|
+
<h4>בדיקת שיחה יזומה</h4>
|
|
113
|
+
<div class="form-inline">
|
|
114
|
+
<div class="form-group">
|
|
115
|
+
<input type="text" class="form-control" id="test-user-call-phone"
|
|
116
|
+
placeholder="05X-XXXXXXX" dir="ltr" style="width: 150px;" />
|
|
117
|
+
</div>
|
|
118
|
+
<button type="button" class="btn btn-info" id="test-user-call-btn">
|
|
119
|
+
<i class="fa fa-phone-square"></i> בדוק שיחה יזומה
|
|
120
|
+
</button>
|
|
121
|
+
<span id="test-user-call-status" style="margin-right: 10px;"></span>
|
|
122
|
+
</div>
|
|
123
|
+
<p class="help-block" style="font-size: 12px; margin-top: 5px;">
|
|
124
|
+
יוצר קוד אימות זמני ומציג אותו למנהל לבדיקה
|
|
125
|
+
</p>
|
|
75
126
|
</div>
|
|
76
|
-
<button type="button" class="btn btn-warning" id="test-call-btn">
|
|
77
|
-
<i class="fa fa-phone"></i> שלח צינתוק בדיקה
|
|
78
|
-
</button>
|
|
79
|
-
<span id="test-status" style="margin-right: 10px;"></span>
|
|
80
127
|
</div>
|
|
81
128
|
</div>
|
|
82
129
|
</div>
|
|
@@ -147,4 +194,4 @@
|
|
|
147
194
|
</div>
|
|
148
195
|
</div>
|
|
149
196
|
</div>
|
|
150
|
-
</div>
|
|
197
|
+
</div>
|
|
@@ -14,10 +14,37 @@
|
|
|
14
14
|
<div class="form-group">
|
|
15
15
|
<label for="voiceServerEnabled">
|
|
16
16
|
<input type="checkbox" id="voiceServerEnabled" name="voiceServerEnabled" />
|
|
17
|
-
|
|
17
|
+
אימות ע"י צינתוק (0.1 יחידות)
|
|
18
18
|
</label>
|
|
19
19
|
<p class="help-block">כאשר מופעל, התוסף ישלח צינתוק (שיחה מנותקת) למשתמש, שיצטרך לאמת את 4 הספרות האחרונות של המספר המתקשר.</p>
|
|
20
20
|
</div>
|
|
21
|
+
|
|
22
|
+
<div class="form-group">
|
|
23
|
+
<label for="userCallEnabled">
|
|
24
|
+
<input type="checkbox" id="userCallEnabled" name="userCallEnabled" />
|
|
25
|
+
אימות ע"י שיחה יזומה מצד המשתמש (חינם)
|
|
26
|
+
</label>
|
|
27
|
+
<p class="help-block">כאשר מופעל, המשתמש יקבל קוד אימות לאחר שיתקשר לקו שלכם.</p>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div class="form-group">
|
|
31
|
+
<label for="userCallNumber">מספר הקו שיוצג למשתמשים</label>
|
|
32
|
+
<input type="text" class="form-control" id="userCallNumber" name="userCallNumber"
|
|
33
|
+
placeholder="לדוגמה: 03-1234567" dir="ltr" />
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<div class="form-group">
|
|
37
|
+
<label for="callApiToken">טוקן ייחודי לפורום</label>
|
|
38
|
+
<div class="input-group">
|
|
39
|
+
<input type="text" class="form-control" id="callApiToken" name="callApiToken" readonly dir="ltr" />
|
|
40
|
+
<span class="input-group-btn">
|
|
41
|
+
<button type="button" class="btn btn-default" id="refresh-call-token-btn">
|
|
42
|
+
<i class="fa fa-refresh"></i> רענן טוקן
|
|
43
|
+
</button>
|
|
44
|
+
</span>
|
|
45
|
+
</div>
|
|
46
|
+
<p class="help-block">טוקן זה משמש לזיהוי הקו שלכם ב־API.</p>
|
|
47
|
+
</div>
|
|
21
48
|
|
|
22
49
|
<div class="form-group">
|
|
23
50
|
<label for="voiceServerApiKey">Token של Call2All</label>
|
|
@@ -67,16 +94,36 @@
|
|
|
67
94
|
|
|
68
95
|
<hr />
|
|
69
96
|
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
<
|
|
74
|
-
|
|
97
|
+
<div class="row">
|
|
98
|
+
<div class="col-md-6">
|
|
99
|
+
<h4>בדיקת צינתוק</h4>
|
|
100
|
+
<div class="form-inline">
|
|
101
|
+
<div class="form-group">
|
|
102
|
+
<input type="text" class="form-control" id="test-phone"
|
|
103
|
+
placeholder="05X-XXXXXXX" dir="ltr" style="width: 150px;" />
|
|
104
|
+
</div>
|
|
105
|
+
<button type="button" class="btn btn-warning" id="test-call-btn">
|
|
106
|
+
<i class="fa fa-phone"></i> שלח צינתוק בדיקה
|
|
107
|
+
</button>
|
|
108
|
+
<span id="test-status" style="margin-right: 10px;"></span>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
<div class="col-md-6">
|
|
112
|
+
<h4>בדיקת שיחה יזומה</h4>
|
|
113
|
+
<div class="form-inline">
|
|
114
|
+
<div class="form-group">
|
|
115
|
+
<input type="text" class="form-control" id="test-user-call-phone"
|
|
116
|
+
placeholder="05X-XXXXXXX" dir="ltr" style="width: 150px;" />
|
|
117
|
+
</div>
|
|
118
|
+
<button type="button" class="btn btn-info" id="test-user-call-btn">
|
|
119
|
+
<i class="fa fa-phone-square"></i> בדוק שיחה יזומה
|
|
120
|
+
</button>
|
|
121
|
+
<span id="test-user-call-status" style="margin-right: 10px;"></span>
|
|
122
|
+
</div>
|
|
123
|
+
<p class="help-block" style="font-size: 12px; margin-top: 5px;">
|
|
124
|
+
יוצר קוד אימות זמני ומציג אותו למנהל לבדיקה
|
|
125
|
+
</p>
|
|
75
126
|
</div>
|
|
76
|
-
<button type="button" class="btn btn-warning" id="test-call-btn">
|
|
77
|
-
<i class="fa fa-phone"></i> שלח צינתוק בדיקה
|
|
78
|
-
</button>
|
|
79
|
-
<span id="test-status" style="margin-right: 10px;"></span>
|
|
80
127
|
</div>
|
|
81
128
|
</div>
|
|
82
129
|
</div>
|
|
@@ -147,4 +194,4 @@
|
|
|
147
194
|
</div>
|
|
148
195
|
</div>
|
|
149
196
|
</div>
|
|
150
|
-
</div>
|
|
197
|
+
</div>
|