nodebb-plugin-phone-verification 2.0.1 → 3.0.1
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 +201 -4
- 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 };
|
|
@@ -511,6 +548,9 @@ plugin.init = async function (params) {
|
|
|
511
548
|
router.post('/api/phone-verification/verify-code', middleware.applyCSRF, plugin.apiVerifyCode);
|
|
512
549
|
router.post('/api/phone-verification/initiate-call', middleware.applyCSRF, plugin.apiInitiateCall);
|
|
513
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);
|
|
514
554
|
|
|
515
555
|
// User Profile APIs
|
|
516
556
|
router.get('/api/user/:userslug/phone', middleware.authenticateRequest, plugin.apiGetUserPhoneProfile);
|
|
@@ -527,6 +567,8 @@ plugin.init = async function (params) {
|
|
|
527
567
|
router.get('/api/admin/plugins/phone-verification/settings', middleware.admin.checkPrivileges, plugin.apiAdminGetSettings);
|
|
528
568
|
router.post('/api/admin/plugins/phone-verification/settings', middleware.admin.checkPrivileges, middleware.applyCSRF, plugin.apiAdminSaveSettings);
|
|
529
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/test-user-call', middleware.admin.checkPrivileges, middleware.applyCSRF, plugin.apiAdminTestUserCall);
|
|
571
|
+
router.post('/api/admin/plugins/phone-verification/refresh-token', middleware.admin.checkPrivileges, middleware.applyCSRF, plugin.apiAdminRefreshToken);
|
|
530
572
|
};
|
|
531
573
|
plugin.apiCheckStatus = async function (req, res) {
|
|
532
574
|
try {
|
|
@@ -540,10 +582,19 @@ plugin.getSettings = async function () {
|
|
|
540
582
|
|
|
541
583
|
const isTrue = (val) => val === true || val === 'true' || val === 'on' || val === '1';
|
|
542
584
|
|
|
585
|
+
if (!settings.callApiToken) {
|
|
586
|
+
const newToken = plugin.generateApiToken();
|
|
587
|
+
await meta.settings.set('phone-verification', { ...settings, callApiToken: newToken });
|
|
588
|
+
settings.callApiToken = newToken;
|
|
589
|
+
}
|
|
590
|
+
|
|
543
591
|
return {
|
|
544
592
|
voiceServerUrl: settings.voiceServerUrl || defaultSettings.voiceServerUrl,
|
|
545
593
|
voiceServerApiKey: settings.voiceServerApiKey || '',
|
|
546
594
|
voiceServerEnabled: isTrue(settings.voiceServerEnabled),
|
|
595
|
+
userCallEnabled: isTrue(settings.userCallEnabled),
|
|
596
|
+
userCallNumber: settings.userCallNumber || '',
|
|
597
|
+
callApiToken: settings.callApiToken || '',
|
|
547
598
|
blockUnverifiedUsers: isTrue(settings.blockUnverifiedUsers)
|
|
548
599
|
};
|
|
549
600
|
};
|
|
@@ -554,6 +605,9 @@ plugin.saveSettings = async function (settings) {
|
|
|
554
605
|
voiceServerUrl: settings.voiceServerUrl || defaultSettings.voiceServerUrl,
|
|
555
606
|
voiceServerApiKey: settings.voiceServerApiKey || '',
|
|
556
607
|
voiceServerEnabled: settings.voiceServerEnabled ? 'true' : 'false',
|
|
608
|
+
userCallEnabled: settings.userCallEnabled ? 'true' : 'false',
|
|
609
|
+
userCallNumber: settings.userCallNumber || '',
|
|
610
|
+
callApiToken: settings.callApiToken || '',
|
|
557
611
|
blockUnverifiedUsers: settings.blockUnverifiedUsers ? 'true' : 'false'
|
|
558
612
|
});
|
|
559
613
|
return true;
|
|
@@ -570,14 +624,24 @@ plugin.apiAdminGetSettings = async function (req, res) {
|
|
|
570
624
|
|
|
571
625
|
plugin.apiAdminSaveSettings = async function (req, res) {
|
|
572
626
|
try {
|
|
573
|
-
const { voiceServerApiKey, ...rest } = req.body;
|
|
627
|
+
const { voiceServerApiKey, callApiToken, ...rest } = req.body;
|
|
574
628
|
const current = await plugin.getSettings();
|
|
575
629
|
const apiKey = voiceServerApiKey === '********' ? current.voiceServerApiKey : voiceServerApiKey;
|
|
576
|
-
|
|
630
|
+
const tokenToSave = callApiToken || current.callApiToken;
|
|
631
|
+
await plugin.saveSettings({ ...rest, voiceServerApiKey: apiKey, callApiToken: tokenToSave });
|
|
577
632
|
res.json({ success: true });
|
|
578
633
|
} catch (err) { res.json({ success: false }); }
|
|
579
634
|
};
|
|
580
635
|
|
|
636
|
+
plugin.apiAdminRefreshToken = async function (req, res) {
|
|
637
|
+
try {
|
|
638
|
+
const current = await plugin.getSettings();
|
|
639
|
+
const newToken = plugin.generateApiToken();
|
|
640
|
+
await plugin.saveSettings({ ...current, callApiToken: newToken });
|
|
641
|
+
res.json({ success: true, token: newToken });
|
|
642
|
+
} catch (err) { res.json({ success: false }); }
|
|
643
|
+
};
|
|
644
|
+
|
|
581
645
|
plugin.apiAdminTestCall = async function (req, res) {
|
|
582
646
|
try {
|
|
583
647
|
const { phoneNumber } = req.body;
|
|
@@ -589,6 +653,41 @@ plugin.apiAdminTestCall = async function (req, res) {
|
|
|
589
653
|
} catch (err) { res.json({ success: false }); }
|
|
590
654
|
};
|
|
591
655
|
|
|
656
|
+
plugin.apiAdminTestUserCall = async function (req, res) {
|
|
657
|
+
try {
|
|
658
|
+
const { phoneNumber } = req.body;
|
|
659
|
+
if (!phoneNumber) return res.json({ success: false, message: 'חסר טלפון' });
|
|
660
|
+
|
|
661
|
+
const settings = await plugin.getSettings();
|
|
662
|
+
if (!settings.userCallEnabled) {
|
|
663
|
+
return res.json({ success: false, message: 'אימות בשיחה יזומה אינו פעיל' });
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
const clean = plugin.normalizePhone(phoneNumber);
|
|
667
|
+
if (!plugin.validatePhoneNumber(clean)) {
|
|
668
|
+
return res.json({ success: false, message: 'מספר טלפון לא תקין' });
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// יצירת קוד אימות זמני לבדיקה
|
|
672
|
+
const code = plugin.generateNumericCode();
|
|
673
|
+
const saveResult = await plugin.saveVerificationCode(clean, code, { storePlain: true });
|
|
674
|
+
|
|
675
|
+
if (!saveResult.success) {
|
|
676
|
+
return res.json({ success: false, message: 'שגיאה בשמירת קוד האימות' });
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
res.json({
|
|
680
|
+
success: true,
|
|
681
|
+
code: code,
|
|
682
|
+
phoneNumber: settings.userCallNumber || 'לא הוגדר',
|
|
683
|
+
message: 'קוד אימות נוצר בהצלחה לבדיקה'
|
|
684
|
+
});
|
|
685
|
+
} catch (err) {
|
|
686
|
+
console.error(err);
|
|
687
|
+
res.json({ success: false, message: 'שגיאה ביצירת קוד האימות' });
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
|
|
592
691
|
plugin.apiSendCode = async function (req, res) {
|
|
593
692
|
try {
|
|
594
693
|
const { phoneNumber } = req.body;
|
|
@@ -624,6 +723,40 @@ plugin.apiSendCode = async function (req, res) {
|
|
|
624
723
|
}
|
|
625
724
|
};
|
|
626
725
|
|
|
726
|
+
plugin.apiRequestUserCall = async function (req, res) {
|
|
727
|
+
try {
|
|
728
|
+
const { phoneNumber } = req.body;
|
|
729
|
+
if (!phoneNumber) return res.json({ success: false, error: 'MISSING' });
|
|
730
|
+
|
|
731
|
+
const settings = await plugin.getSettings();
|
|
732
|
+
if (!settings.userCallEnabled) {
|
|
733
|
+
return res.json({ success: false, error: 'CALL_DISABLED', message: 'אימות בשיחה יזומה אינו פעיל' });
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
const clientIp = req.ip || req.headers['x-forwarded-for'];
|
|
737
|
+
const ipCheck = await plugin.checkIpRateLimit(clientIp);
|
|
738
|
+
if (!ipCheck.allowed) return res.json(ipCheck);
|
|
739
|
+
await plugin.incrementIpCounter(clientIp);
|
|
740
|
+
|
|
741
|
+
const clean = plugin.normalizePhone(phoneNumber.replace(/\D/g, ''));
|
|
742
|
+
if (!plugin.validatePhoneNumber(clean)) return res.json({ success: false, error: 'INVALID' });
|
|
743
|
+
|
|
744
|
+
const existingUid = await plugin.findUserByPhone(clean);
|
|
745
|
+
if (existingUid && (!req.uid || parseInt(existingUid) !== parseInt(req.uid))) {
|
|
746
|
+
return res.json({ success: false, error: 'EXISTS', message: 'המספר תפוס' });
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
const code = plugin.generateNumericCode();
|
|
750
|
+
const saveResult = await plugin.saveVerificationCode(clean, code, { storePlain: true });
|
|
751
|
+
if (!saveResult.success) return res.json(saveResult);
|
|
752
|
+
|
|
753
|
+
res.json({ success: true, message: 'הקוד הוכן. נא להתקשר לקו לצורך שמיעת הקוד.', callNumber: settings.userCallNumber || '' });
|
|
754
|
+
} catch (err) {
|
|
755
|
+
console.error(err);
|
|
756
|
+
res.json({ success: false });
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
|
|
627
760
|
plugin.apiVerifyCode = async function (req, res) {
|
|
628
761
|
try {
|
|
629
762
|
const { phoneNumber, code } = req.body;
|
|
@@ -677,6 +810,70 @@ plugin.apiInitiateCall = async function (req, res) {
|
|
|
677
810
|
}
|
|
678
811
|
};
|
|
679
812
|
|
|
813
|
+
plugin.apiInboundCall = async function (req, res) {
|
|
814
|
+
try {
|
|
815
|
+
const settings = await plugin.getSettings();
|
|
816
|
+
const token = req.query.token;
|
|
817
|
+
const phone = req.query.ApiPhone;
|
|
818
|
+
|
|
819
|
+
if (!token || token !== settings.callApiToken) {
|
|
820
|
+
const errorPayload = 'read=t-שגיאת הרשאה - הטוקן אינו תקין. אנא פנו למנהל המערכת. לבדיקה חוזרת הַקִּישׁוּ 1=MOP,,1,1,15,NO,,,,1,3,OK,,,no';
|
|
821
|
+
res.set('Content-Type', 'text/plain; charset=utf-8');
|
|
822
|
+
return res.status(403).send(errorPayload);
|
|
823
|
+
}
|
|
824
|
+
if (!phone || !plugin.validatePhoneNumber(phone)) {
|
|
825
|
+
const errorPayload = 'read=t-מספר הטלפון אינו תקין או חסר. אנא פנו למנהל המערכת. לבדיקה חוזרת הַקִּישׁוּ 1=MOP,,1,1,15,NO,,,,1,3,OK,,,no';
|
|
826
|
+
res.set('Content-Type', 'text/plain; charset=utf-8');
|
|
827
|
+
return res.status(400).send(errorPayload);
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
const pending = await plugin.getPendingPlainCode(phone);
|
|
831
|
+
if (!pending.success) {
|
|
832
|
+
let errorMessage = 'קוד האימות לא נמצא או פג תוקפו';
|
|
833
|
+
|
|
834
|
+
// הודעות ספציפיות לפי סוג השגיאה
|
|
835
|
+
if (pending.error === 'CODE_EXPIRED') {
|
|
836
|
+
errorMessage = 'פג תוקף קוד האימות';
|
|
837
|
+
} else if (pending.error === 'PHONE_BLOCKED') {
|
|
838
|
+
errorMessage = 'המספר חסום זמנית';
|
|
839
|
+
} else if (pending.error === 'CODE_UNAVAILABLE') {
|
|
840
|
+
errorMessage = 'הקוד אינו זמין כרגע';
|
|
841
|
+
} else if (pending.error === 'DB_ERROR') {
|
|
842
|
+
errorMessage = 'שגיאה במסד הנתונים';
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
const errorPayload = `read=t-${errorMessage}. אנא פנו למנהל המערכת. לבדיקה חוזרת הַקִּישׁוּ 1=MOP,,1,1,15,NO,,,,1,3,OK,,,no`;
|
|
846
|
+
res.set('Content-Type', 'text/plain; charset=utf-8');
|
|
847
|
+
return res.status(404).send(errorPayload);
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
const code = pending.code;
|
|
851
|
+
const payload = `read=t-הקוד שלכם החד פעמי הוא.d-${code}.t-לשמיעה חוזרת הַקִּישׁוּ 1=MOP,,1,1,15,NO,,,,1,3,OK,,,no`;
|
|
852
|
+
res.set('Content-Type', 'text/plain; charset=utf-8');
|
|
853
|
+
res.send(payload);
|
|
854
|
+
} catch (err) {
|
|
855
|
+
const errorPayload = 'read=t-שגיאה כללית במערכת. אנא פנו למנהל המערכת. לבדיקה חוזרת הַקִּישׁוּ 1=MOP,,1,1,15,NO,,,,1,3,OK,,,no';
|
|
856
|
+
res.set('Content-Type', 'text/plain; charset=utf-8');
|
|
857
|
+
res.status(500).send(errorPayload);
|
|
858
|
+
}
|
|
859
|
+
};
|
|
860
|
+
|
|
861
|
+
plugin.apiGetPublicSettings = async function (req, res) {
|
|
862
|
+
try {
|
|
863
|
+
const settings = await plugin.getSettings();
|
|
864
|
+
res.json({
|
|
865
|
+
success: true,
|
|
866
|
+
settings: {
|
|
867
|
+
voiceServerEnabled: settings.voiceServerEnabled,
|
|
868
|
+
userCallEnabled: settings.userCallEnabled,
|
|
869
|
+
userCallNumber: settings.userCallNumber || ''
|
|
870
|
+
}
|
|
871
|
+
});
|
|
872
|
+
} catch (e) {
|
|
873
|
+
res.json({ success: false });
|
|
874
|
+
}
|
|
875
|
+
};
|
|
876
|
+
|
|
680
877
|
plugin.apiGetUserPhoneProfile = async function (req, res) {
|
|
681
878
|
try {
|
|
682
879
|
const uid = await User.getUidByUserslug(req.params.userslug);
|
|
@@ -775,4 +972,4 @@ plugin.userDelete = async function (data) {
|
|
|
775
972
|
} catch (e) {}
|
|
776
973
|
};
|
|
777
974
|
|
|
778
|
-
module.exports = plugin;
|
|
975
|
+
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.1",
|
|
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>
|