nodebb-plugin-phone-verification 1.2.2
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 +758 -0
- package/package.json +30 -0
- package/plugin.json +32 -0
- package/static/lib/admin.js +187 -0
- package/static/lib/main.js +456 -0
- package/templates/admin/plugins/phone-verification.tpl +163 -0
- package/templates/admin/settings/phone-verification.tpl +163 -0
- package/test/helpers.test.js +158 -0
- package/test/phone-storage.test.js +216 -0
- package/test/verification.test.js +173 -0
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
define('forum/phone-verification', ['hooks', 'translator'], function (hooks, translator) {
|
|
4
|
+
|
|
5
|
+
function isValidIsraeliPhone(phone) {
|
|
6
|
+
if (!phone) return false;
|
|
7
|
+
const cleanPhone = phone.replace(/[-\s]/g, '');
|
|
8
|
+
return /^05\d{8}$/.test(cleanPhone);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// ==================== לוגיקה לעמוד הרשמה (Registration) ====================
|
|
12
|
+
|
|
13
|
+
const Registration = {
|
|
14
|
+
resendTimer: null,
|
|
15
|
+
resendCountdown: 0,
|
|
16
|
+
phoneVerified: false,
|
|
17
|
+
|
|
18
|
+
validatePhone: function(phone) {
|
|
19
|
+
return isValidIsraeliPhone(phone);
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
showError: function(message) {
|
|
23
|
+
$('#phone-error').text(message).removeClass('hidden').show();
|
|
24
|
+
$('#phone-success').addClass('hidden').hide();
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
showSuccess: function(message) {
|
|
28
|
+
$('#phone-success').text(message).removeClass('hidden').show();
|
|
29
|
+
$('#phone-error').addClass('hidden').hide();
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
hideMessages: function() {
|
|
33
|
+
$('#phone-error').addClass('hidden').hide();
|
|
34
|
+
$('#phone-success').addClass('hidden').hide();
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
startResendTimer: function() {
|
|
38
|
+
const self = this;
|
|
39
|
+
self.resendCountdown = 60;
|
|
40
|
+
self.updateResendButton();
|
|
41
|
+
|
|
42
|
+
self.resendTimer = setInterval(function () {
|
|
43
|
+
self.resendCountdown--;
|
|
44
|
+
self.updateResendButton();
|
|
45
|
+
|
|
46
|
+
if (self.resendCountdown <= 0) {
|
|
47
|
+
clearInterval(self.resendTimer);
|
|
48
|
+
self.resendTimer = null;
|
|
49
|
+
}
|
|
50
|
+
}, 1000);
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
updateResendButton: function() {
|
|
54
|
+
const $btn = $('#resend-code-btn');
|
|
55
|
+
if (this.resendCountdown > 0) {
|
|
56
|
+
$btn.prop('disabled', true).text('שלח שוב (' + this.resendCountdown + ')');
|
|
57
|
+
} else {
|
|
58
|
+
$btn.prop('disabled', false).text('שלח קוד שוב');
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
init: function() {
|
|
63
|
+
const self = this;
|
|
64
|
+
const $form = $('[component="register/local"]');
|
|
65
|
+
if (!$form.length) return;
|
|
66
|
+
if ($('#phoneNumber').length) return; // מניעת כפילות
|
|
67
|
+
|
|
68
|
+
// איפוס משתנים
|
|
69
|
+
self.phoneVerified = false;
|
|
70
|
+
|
|
71
|
+
const phoneHtml = `
|
|
72
|
+
<div class="mb-2 d-flex flex-column gap-2" id="phone-verification-container">
|
|
73
|
+
<label for="phoneNumber">מספר טלפון <span class="text-danger">*</span></label>
|
|
74
|
+
<div class="d-flex flex-column">
|
|
75
|
+
<div class="input-group">
|
|
76
|
+
<input class="form-control" type="tel" name="phoneNumber" id="phoneNumber"
|
|
77
|
+
placeholder="05X-XXXXXXX" dir="ltr" autocomplete="tel" />
|
|
78
|
+
<button class="btn btn-primary" type="button" id="send-code-btn">
|
|
79
|
+
<i class="fa fa-phone"></i> שלח קוד
|
|
80
|
+
</button>
|
|
81
|
+
</div>
|
|
82
|
+
<span class="form-text text-xs">תקבל שיחה קולית עם קוד אימות</span>
|
|
83
|
+
<div id="phone-error" class="text-danger text-xs hidden"></div>
|
|
84
|
+
<div id="phone-success" class="text-success text-xs hidden"></div>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<div class="mb-2 d-flex flex-column gap-2 hidden" id="verification-code-container">
|
|
89
|
+
<label for="verificationCode">קוד אימות</label>
|
|
90
|
+
<div class="d-flex flex-column">
|
|
91
|
+
<div class="input-group">
|
|
92
|
+
<input class="form-control" type="text" id="verificationCode"
|
|
93
|
+
placeholder="הזן קוד 6 ספרות" maxlength="6" dir="ltr" />
|
|
94
|
+
<button class="btn btn-success" type="button" id="verify-code-btn">
|
|
95
|
+
<i class="fa fa-check"></i> אמת
|
|
96
|
+
</button>
|
|
97
|
+
</div>
|
|
98
|
+
<button class="btn btn-link btn-sm p-0 text-start" type="button" id="resend-code-btn">
|
|
99
|
+
שלח קוד שוב
|
|
100
|
+
</button>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
<div id="phone-verified-badge" class="alert alert-success hidden">
|
|
105
|
+
<i class="fa fa-check-circle"></i> מספר הטלפון אומת בהצלחה!
|
|
106
|
+
</div>
|
|
107
|
+
`;
|
|
108
|
+
|
|
109
|
+
const $registerBtn = $form.find('button[type="submit"]');
|
|
110
|
+
if ($registerBtn.length) {
|
|
111
|
+
$(phoneHtml).insertBefore($registerBtn);
|
|
112
|
+
} else {
|
|
113
|
+
$form.append(phoneHtml);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
self.attachEventListeners();
|
|
117
|
+
self.checkExistingVerification();
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
attachEventListeners: function() {
|
|
121
|
+
const self = this;
|
|
122
|
+
|
|
123
|
+
// שליחת קוד
|
|
124
|
+
$('#send-code-btn').off('click').on('click', function () {
|
|
125
|
+
const phone = $('#phoneNumber').val().trim();
|
|
126
|
+
self.hideMessages();
|
|
127
|
+
|
|
128
|
+
// if (!isValidIsraeliPhone(phone)) {
|
|
129
|
+
// self.showError('מספר הטלפון אינו תקין (05X-XXXXXXX)');
|
|
130
|
+
// return;
|
|
131
|
+
// }
|
|
132
|
+
|
|
133
|
+
const $btn = $(this);
|
|
134
|
+
$btn.prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i> שולח...');
|
|
135
|
+
|
|
136
|
+
$.ajax({
|
|
137
|
+
url: config.relative_path + '/api/phone-verification/send-code',
|
|
138
|
+
method: 'POST',
|
|
139
|
+
data: { phoneNumber: phone },
|
|
140
|
+
headers: { 'x-csrf-token': config.csrf_token },
|
|
141
|
+
success: function (response) {
|
|
142
|
+
if (response.success) {
|
|
143
|
+
self.showSuccess('קוד אימות נשלח!');
|
|
144
|
+
$('#verification-code-container').removeClass('hidden');
|
|
145
|
+
$('#phoneNumber').prop('readonly', true);
|
|
146
|
+
$btn.addClass('hidden');
|
|
147
|
+
self.startResendTimer();
|
|
148
|
+
} else {
|
|
149
|
+
self.showError(response.message || 'שגיאה בשליחה');
|
|
150
|
+
$btn.prop('disabled', false).html('<i class="fa fa-phone"></i> שלח קוד');
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
error: function() {
|
|
154
|
+
self.showError('שגיאת תקשורת');
|
|
155
|
+
$btn.prop('disabled', false).html('<i class="fa fa-phone"></i> שלח קוד');
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// אימות קוד
|
|
161
|
+
$('#verify-code-btn').off('click').on('click', function () {
|
|
162
|
+
const phone = $('#phoneNumber').val().trim();
|
|
163
|
+
const code = $('#verificationCode').val().trim();
|
|
164
|
+
self.hideMessages();
|
|
165
|
+
|
|
166
|
+
if (!code || code.length < 4) {
|
|
167
|
+
self.showError('קוד לא תקין');
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
$.ajax({
|
|
172
|
+
url: config.relative_path + '/api/phone-verification/verify-code',
|
|
173
|
+
method: 'POST',
|
|
174
|
+
data: { phoneNumber: phone, code: code },
|
|
175
|
+
headers: { 'x-csrf-token': config.csrf_token },
|
|
176
|
+
success: function (response) {
|
|
177
|
+
if (response.success) {
|
|
178
|
+
self.phoneVerified = true;
|
|
179
|
+
$('#verification-code-container, #phone-verification-container').addClass('hidden');
|
|
180
|
+
$('#phone-verified-badge').removeClass('hidden');
|
|
181
|
+
|
|
182
|
+
// טיפול בשדה הנסתר לטופס
|
|
183
|
+
$('#phoneNumber').prop('disabled', true).removeAttr('name');
|
|
184
|
+
if (!$('#phoneNumberVerified').length) {
|
|
185
|
+
$('<input>').attr({type: 'hidden', name: 'phoneNumber', id: 'phoneNumberVerified', value: phone}).appendTo('[component="register/local"]');
|
|
186
|
+
} else {
|
|
187
|
+
$('#phoneNumberVerified').val(phone);
|
|
188
|
+
}
|
|
189
|
+
} else {
|
|
190
|
+
self.showError(response.message || 'קוד שגוי');
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// חסימת שליחת טופס
|
|
197
|
+
$('[component="register/local"]').off('submit.phone').on('submit.phone', function (e) {
|
|
198
|
+
if (!self.phoneVerified) {
|
|
199
|
+
e.preventDefault();
|
|
200
|
+
e.stopPropagation();
|
|
201
|
+
self.showError('יש לאמת את מספר הטלפון לפני ההרשמה');
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
checkExistingVerification: function() {
|
|
208
|
+
const phone = $('#phoneNumber').val();
|
|
209
|
+
if (phone && this.validatePhone(phone)) {
|
|
210
|
+
// לוגיקה לבדיקה אם כבר אומת
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
// ==================== לוגיקה לעריכת פרופיל (Edit Profile) ====================
|
|
216
|
+
|
|
217
|
+
function handleProfileEdit() {
|
|
218
|
+
// בדיקת כפילות קפדנית לפי ID
|
|
219
|
+
if ($('#sidebar-phone-li').length > 0) return;
|
|
220
|
+
|
|
221
|
+
const userslug = ajaxify.data.userslug;
|
|
222
|
+
|
|
223
|
+
// שליפת הנתונים הנוכחיים
|
|
224
|
+
$.getJSON(config.relative_path + '/api/user/' + userslug + '/phone', function (response) {
|
|
225
|
+
// בדיקה נוספת בתוך ה-callback למקרה של טעינה כפולה מהירה
|
|
226
|
+
if ($('#sidebar-phone-li').length > 0) return;
|
|
227
|
+
|
|
228
|
+
if (!response.success) return;
|
|
229
|
+
|
|
230
|
+
if (response.phone && response.phoneVerified) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const hasPhone = response.phone && response.phone.length > 0;
|
|
235
|
+
|
|
236
|
+
// עדכון הטקסט שיהיה ברור יותר
|
|
237
|
+
const buttonLabel = hasPhone ? 'אמת מספר טלפון' : 'הוסף מספר טלפון';
|
|
238
|
+
|
|
239
|
+
const menuHtml = `
|
|
240
|
+
<li class="list-group-item" id="sidebar-phone-li">
|
|
241
|
+
<a href="#" id="sidebar-phone-link" class="text-decoration-none text-reset">
|
|
242
|
+
${buttonLabel}
|
|
243
|
+
</a>
|
|
244
|
+
</li>
|
|
245
|
+
`;
|
|
246
|
+
const $passwordLink = $('a[href$="/edit/password"]');
|
|
247
|
+
|
|
248
|
+
if ($passwordLink.length) {
|
|
249
|
+
$passwordLink.closest('li').after(menuHtml);
|
|
250
|
+
} else {
|
|
251
|
+
$('.list-group').first().append(menuHtml);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
$('#sidebar-phone-link').off('click').on('click', function(e) {
|
|
255
|
+
e.preventDefault();
|
|
256
|
+
openPhoneManagementModal(response.phone, response.phoneVerified, userslug);
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// פונקציה חדשה לניהול החלונית הקופצת בעריכה
|
|
262
|
+
function openPhoneManagementModal(currentPhone, isVerified, userslug) {
|
|
263
|
+
const phoneVal = currentPhone || '';
|
|
264
|
+
|
|
265
|
+
const modalHtml = `
|
|
266
|
+
<div class="phone-modal-content">
|
|
267
|
+
<div class="mb-3">
|
|
268
|
+
<label class="form-label fw-bold">מספר טלפון נייד</label>
|
|
269
|
+
<div class="input-group">
|
|
270
|
+
<input class="form-control" type="tel" id="modal-phoneNumber" value="${phoneVal}" placeholder="05X-XXXXXXX" dir="ltr">
|
|
271
|
+
</div>
|
|
272
|
+
<div class="form-text text-muted mt-2">
|
|
273
|
+
<i class="fa fa-info-circle"></i>
|
|
274
|
+
${isVerified
|
|
275
|
+
? 'המספר הנוכחי מאומת. שינוי המספר יחייב אימות מחדש.'
|
|
276
|
+
: 'יש להזין מספר ולקבל שיחה קולית לאימות.'}
|
|
277
|
+
</div>
|
|
278
|
+
</div>
|
|
279
|
+
<div id="modal-alert-area"></div>
|
|
280
|
+
</div>
|
|
281
|
+
`;
|
|
282
|
+
|
|
283
|
+
const dialog = bootbox.dialog({
|
|
284
|
+
title: isVerified ? 'שינוי מספר טלפון' : 'עדכון מספר טלפון',
|
|
285
|
+
message: modalHtml,
|
|
286
|
+
buttons: {
|
|
287
|
+
cancel: {
|
|
288
|
+
label: 'ביטול',
|
|
289
|
+
className: 'btn-ghost'
|
|
290
|
+
},
|
|
291
|
+
verify: {
|
|
292
|
+
label: 'שלח קוד אימות',
|
|
293
|
+
className: 'btn-primary',
|
|
294
|
+
callback: function() {
|
|
295
|
+
const newPhone = $('#modal-phoneNumber').val();
|
|
296
|
+
|
|
297
|
+
// שימוש בפונקציית הבדיקה המתוקנת
|
|
298
|
+
if (!isValidIsraeliPhone(newPhone)) {
|
|
299
|
+
showModalAlert('נא להזין מספר תקין (05X-XXXXXXX)', 'danger');
|
|
300
|
+
return false; // מונע סגירה
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// התחלת תהליך האימות
|
|
304
|
+
performPhoneUpdate(newPhone, userslug, dialog);
|
|
305
|
+
return false; // מונע סגירה אוטומטית
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function showModalAlert(msg, type) {
|
|
313
|
+
const html = `<div class="alert alert-${type} p-2 mt-2">${msg}</div>`;
|
|
314
|
+
$('#modal-alert-area').html(html);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function performPhoneUpdate(phone, userslug, dialog) {
|
|
318
|
+
const $btn = dialog.find('.bootbox-accept'); // הכפתור הכחול
|
|
319
|
+
$btn.prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i> שולח...');
|
|
320
|
+
|
|
321
|
+
// 1. שמירה זמנית
|
|
322
|
+
$.post(config.relative_path + '/api/user/' + userslug + '/phone', {
|
|
323
|
+
phoneNumber: phone,
|
|
324
|
+
_csrf: config.csrf_token
|
|
325
|
+
}, function(res) {
|
|
326
|
+
if (!res.success) {
|
|
327
|
+
showModalAlert(res.message || res.error, 'danger');
|
|
328
|
+
$btn.prop('disabled', false).text('שלח קוד אימות');
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// 2. שליחת השיחה
|
|
333
|
+
$.post(config.relative_path + '/api/phone-verification/send-code', {
|
|
334
|
+
phoneNumber: phone,
|
|
335
|
+
_csrf: config.csrf_token
|
|
336
|
+
}, function(callRes) {
|
|
337
|
+
if (callRes.success) {
|
|
338
|
+
dialog.modal('hide'); // סגירת החלון הראשון
|
|
339
|
+
|
|
340
|
+
// 3. פתיחת חלון הזנת קוד
|
|
341
|
+
bootbox.prompt({
|
|
342
|
+
title: "הזן את הקוד שקיבלת בשיחה",
|
|
343
|
+
inputType: 'number',
|
|
344
|
+
callback: function (code) {
|
|
345
|
+
if (!code) return;
|
|
346
|
+
|
|
347
|
+
$.post(config.relative_path + '/api/user/' + userslug + '/phone/verify', {
|
|
348
|
+
code: code,
|
|
349
|
+
_csrf: config.csrf_token
|
|
350
|
+
}, function(verifyRes){
|
|
351
|
+
if(verifyRes.success) {
|
|
352
|
+
app.alertSuccess('הטלפון עודכן ואומת בהצלחה!');
|
|
353
|
+
ajaxify.refresh(); // רענון הדף
|
|
354
|
+
} else {
|
|
355
|
+
app.alertError(verifyRes.message || 'קוד שגוי');
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
} else {
|
|
361
|
+
showModalAlert(callRes.message || 'שגיאה בשליחת השיחה', 'danger');
|
|
362
|
+
$btn.prop('disabled', false).text('שלח קוד אימות');
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// ==================== לוגיקה לצפייה בפרופיל (View Profile) ====================
|
|
369
|
+
|
|
370
|
+
function handleProfileView() {
|
|
371
|
+
// בדיקה ראשונית למניעת כפילות עוד לפני הקריאה לשרת
|
|
372
|
+
if ($('#user-phone-stat-item').length > 0) return;
|
|
373
|
+
|
|
374
|
+
const userslug = ajaxify.data.userslug;
|
|
375
|
+
|
|
376
|
+
$.getJSON(config.relative_path + '/api/user/' + userslug + '/phone', function (response) {
|
|
377
|
+
if (!response.success) return;
|
|
378
|
+
|
|
379
|
+
// אם אין מספר טלפון - לא מציגים את הקוביה בכלל (גם לא לבעל הפרופיל)
|
|
380
|
+
if (!response.phone) return;
|
|
381
|
+
|
|
382
|
+
// בדיקה נוספת למניעת כפילות
|
|
383
|
+
if ($('#user-phone-stat-item').length > 0) return;
|
|
384
|
+
|
|
385
|
+
const verifyBadge = response.phoneVerified
|
|
386
|
+
? '<i class="" title=""></i>'
|
|
387
|
+
: '<i class="fa fa-exclamation-triangle text-warning" title="לא מאומת" style="cursor:pointer;" onclick="location.href=\'/user/'+userslug+'/edit\'"></i>';
|
|
388
|
+
|
|
389
|
+
const privacyLabel = response.isOwner
|
|
390
|
+
? ' <span class="text-lowercase">(מוסתר)</span>'
|
|
391
|
+
: '';
|
|
392
|
+
|
|
393
|
+
const phoneText = response.phone;
|
|
394
|
+
|
|
395
|
+
const html = `
|
|
396
|
+
<div class="stat" id="user-phone-stat-item">
|
|
397
|
+
<div class="align-items-center justify-content-center card card-header p-3 border-0 rounded-1 h-100 gap-2">
|
|
398
|
+
<span class="stat-label text-xs fw-semibold">
|
|
399
|
+
<i class="text-muted fa-solid fa-phone"></i>
|
|
400
|
+
<span>מספר טלפון</span>${privacyLabel}
|
|
401
|
+
</span>
|
|
402
|
+
<span class="text-sm text-center text-break w-100 px-2 ff-secondary" dir="ltr">
|
|
403
|
+
${phoneText} ${verifyBadge}
|
|
404
|
+
</span>
|
|
405
|
+
</div>
|
|
406
|
+
</div>
|
|
407
|
+
`;
|
|
408
|
+
|
|
409
|
+
const $statsRow = $('.account-stats .row');
|
|
410
|
+
if ($statsRow.length) {
|
|
411
|
+
$statsRow.append(html);
|
|
412
|
+
} else {
|
|
413
|
+
if ($('.profile-meta').length) {
|
|
414
|
+
$('.profile-meta').append(html);
|
|
415
|
+
} else if ($('.fullname').length) {
|
|
416
|
+
$('.fullname').after(html);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// ==================== ראשי - ניתוב לפי דף ====================
|
|
423
|
+
|
|
424
|
+
const Plugin = {};
|
|
425
|
+
|
|
426
|
+
Plugin.init = function () {
|
|
427
|
+
checkRoute();
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
function checkRoute() {
|
|
431
|
+
if (!ajaxify.data.template) return;
|
|
432
|
+
|
|
433
|
+
// 1. דף הרשמה
|
|
434
|
+
if (ajaxify.data.template.name === 'register' || ajaxify.data.template.name === 'registerComplete') {
|
|
435
|
+
Registration.init();
|
|
436
|
+
}
|
|
437
|
+
// 2. דף עריכת פרופיל
|
|
438
|
+
else if (ajaxify.data.template.name === 'account/edit') {
|
|
439
|
+
handleProfileEdit();
|
|
440
|
+
}
|
|
441
|
+
// 3. דף צפייה בפרופיל
|
|
442
|
+
else if (ajaxify.data.template.name === 'account/profile') {
|
|
443
|
+
handleProfileView();
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
hooks.on('action:ajaxify.end', function (data) {
|
|
448
|
+
checkRoute();
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
if (typeof ajaxify !== 'undefined' && ajaxify.data) {
|
|
452
|
+
checkRoute();
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
return Plugin;
|
|
456
|
+
});
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
<div class="acp-page-container">
|
|
2
|
+
<div class="row">
|
|
3
|
+
<div class="col-lg-12">
|
|
4
|
+
|
|
5
|
+
<div class="panel panel-primary">
|
|
6
|
+
<div class="panel-heading">
|
|
7
|
+
<h3 class="panel-title">
|
|
8
|
+
<i class="fa fa-cog"></i> הגדרות Call2All - שיחות קוליות
|
|
9
|
+
</h3>
|
|
10
|
+
</div>
|
|
11
|
+
<div class="panel-body">
|
|
12
|
+
<form id="voice-settings-form">
|
|
13
|
+
<div class="form-group">
|
|
14
|
+
<label for="voiceServerEnabled">
|
|
15
|
+
<input type="checkbox" id="voiceServerEnabled" name="voiceServerEnabled" />
|
|
16
|
+
הפעל שיחות קוליות
|
|
17
|
+
</label>
|
|
18
|
+
<p class="help-block">כאשר מופעל, התוסף ישלח שיחה קולית עם קוד האימות דרך Call2All</p>
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<div class="form-group">
|
|
22
|
+
<label for="voiceServerApiKey">Token של Call2All</label>
|
|
23
|
+
<input type="password" class="form-control" id="voiceServerApiKey" name="voiceServerApiKey"
|
|
24
|
+
placeholder="WU1BUElL.apik_xxxxx..." dir="ltr" />
|
|
25
|
+
<p class="help-block">ה-Token שקיבלת מ-Call2All (מתחיל ב-WU1BUElL)</p>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<div class="form-group">
|
|
29
|
+
<details style="border: 1px solid #ddd; padding: 10px; border-radius: 4px; background-color: #f9f9f9;">
|
|
30
|
+
<summary style="cursor: pointer; font-weight: bold; color: #337ab7; outline: none;">
|
|
31
|
+
<i class="fa fa-cogs"></i> הגדרות מתקדמות (עריכת פרמטרים ותוכן ההודעה)
|
|
32
|
+
</summary>
|
|
33
|
+
<div style="margin-top: 15px; padding-left: 10px; border-left: 3px solid #337ab7;">
|
|
34
|
+
<div class="form-group">
|
|
35
|
+
<label for="voiceServerUrl">כתובת ה-API (Endpoint)</label>
|
|
36
|
+
<input type="text" class="form-control" id="voiceServerUrl" name="voiceServerUrl"
|
|
37
|
+
placeholder="https://www.call2all.co.il/ym/api/RunCampaign" dir="ltr" />
|
|
38
|
+
<p class="help-block">כתובת השרת אליו נשלחת הבקשה.</p>
|
|
39
|
+
</div>
|
|
40
|
+
<div class="form-group">
|
|
41
|
+
<label for="voiceTtsMode">מצב ה-TTS (ttsMode)</label>
|
|
42
|
+
<input type="text" class="form-control" id="voiceTtsMode" name="voiceTtsMode"
|
|
43
|
+
placeholder="1" dir="ltr" />
|
|
44
|
+
<p class="help-block">ערך הפרמטר <code>ttsMode</code> הנשלח ל-API (ברירת מחדל: 1).</p>
|
|
45
|
+
</div>
|
|
46
|
+
<div class="form-group">
|
|
47
|
+
<label for="voiceMessageTemplate">תוכן ההודעה (Template)</label>
|
|
48
|
+
<textarea class="form-control" id="voiceMessageTemplate" name="voiceMessageTemplate" rows="3" dir="rtl"></textarea>
|
|
49
|
+
<p class="help-block">
|
|
50
|
+
הטקסט שיוקרא למשתמש.<br/>
|
|
51
|
+
Placeholders חובה: <code>{code}</code> (הקוד), <code>{siteTitle}</code> (שם האתר)
|
|
52
|
+
</p>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</details>
|
|
56
|
+
</div>
|
|
57
|
+
<hr />
|
|
58
|
+
|
|
59
|
+
<div class="form-group">
|
|
60
|
+
<div class="checkbox">
|
|
61
|
+
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
|
|
62
|
+
<input type="checkbox" class="mdl-switch__input" id="blockUnverifiedUsers" name="blockUnverifiedUsers">
|
|
63
|
+
<span class="mdl-switch__label"><strong>חסום כתיבה למשתמשים לא מאומתים</strong></span>
|
|
64
|
+
</label>
|
|
65
|
+
</div>
|
|
66
|
+
<p class="help-block">
|
|
67
|
+
אם מופעל, משתמשים רשומים שלא אימתו את הטלפון שלהם לא יוכלו לפתוח נושאים חדשים או להגיב.
|
|
68
|
+
</p>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<div class="form-group">
|
|
72
|
+
<button type="submit" class="btn btn-primary" id="save-settings-btn">
|
|
73
|
+
<i class="fa fa-save"></i> שמור הגדרות
|
|
74
|
+
</button>
|
|
75
|
+
<span id="settings-status" class="text-success" style="margin-right: 10px; display: none;">
|
|
76
|
+
<i class="fa fa-check"></i> נשמר!
|
|
77
|
+
</span>
|
|
78
|
+
</div>
|
|
79
|
+
</form>
|
|
80
|
+
|
|
81
|
+
<hr />
|
|
82
|
+
|
|
83
|
+
<h4>בדיקת שיחה</h4>
|
|
84
|
+
<div class="form-inline">
|
|
85
|
+
<div class="form-group">
|
|
86
|
+
<input type="text" class="form-control" id="test-phone"
|
|
87
|
+
placeholder="05X-XXXXXXX" dir="ltr" style="width: 150px;" />
|
|
88
|
+
</div>
|
|
89
|
+
<button type="button" class="btn btn-warning" id="test-call-btn">
|
|
90
|
+
<i class="fa fa-phone"></i> שלח שיחת בדיקה
|
|
91
|
+
</button>
|
|
92
|
+
<span id="test-status" style="margin-right: 10px;"></span>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<div class="panel panel-default">
|
|
98
|
+
<div class="panel-heading">
|
|
99
|
+
<h3 class="panel-title"><i class="fa fa-phone"></i> ניהול אימות טלפון</h3>
|
|
100
|
+
</div>
|
|
101
|
+
<div class="panel-body">
|
|
102
|
+
|
|
103
|
+
<div class="well">
|
|
104
|
+
<div class="row">
|
|
105
|
+
<div class="col-md-6">
|
|
106
|
+
<h4>חיפוש משתמש לפי מספר טלפון</h4>
|
|
107
|
+
<div class="form-group">
|
|
108
|
+
<div class="input-group">
|
|
109
|
+
<input type="text" class="form-control" id="phone-search" placeholder="הזן מספר טלפון" dir="ltr">
|
|
110
|
+
<span class="input-group-btn">
|
|
111
|
+
<button class="btn btn-primary" type="button" id="search-btn"><i class="fa fa-search"></i> חפש</button>
|
|
112
|
+
</span>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
<div class="col-md-6 text-left" style="padding-top: 35px;">
|
|
117
|
+
<button class="btn btn-success" id="btn-add-manual-user">
|
|
118
|
+
<i class="fa fa-plus"></i> הוסף משתמש מאומת ידנית
|
|
119
|
+
</button>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
<div id="search-result" style="display:none;"><div class="alert" id="search-alert"></div></div>
|
|
123
|
+
</div>
|
|
124
|
+
|
|
125
|
+
<div class="row" style="margin-bottom: 20px;">
|
|
126
|
+
<div class="col-md-4">
|
|
127
|
+
<div class="panel panel-info">
|
|
128
|
+
<div class="panel-heading"><h4 class="panel-title">סה"כ משתמשים עם טלפון</h4></div>
|
|
129
|
+
<div class="panel-body text-center"><h2 id="total-users">0</h2></div>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
<div class="col-md-4">
|
|
133
|
+
<div class="panel panel-success">
|
|
134
|
+
<div class="panel-heading"><h4 class="panel-title">משתמשים מאומתים</h4></div>
|
|
135
|
+
<div class="panel-body text-center"><h2 id="verified-count">0</h2></div>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
|
|
140
|
+
<h4>רשימת משתמשים</h4>
|
|
141
|
+
<div class="table-responsive">
|
|
142
|
+
<table class="table table-striped table-hover" id="users-table">
|
|
143
|
+
<thead>
|
|
144
|
+
<tr>
|
|
145
|
+
<th>מזהה (UID)</th>
|
|
146
|
+
<th>שם משתמש</th>
|
|
147
|
+
<th>מספר טלפון</th>
|
|
148
|
+
<th>תאריך אימות</th>
|
|
149
|
+
<th>סטטוס</th>
|
|
150
|
+
<th class="text-right">פעולות לניהול</th>
|
|
151
|
+
</tr>
|
|
152
|
+
</thead>
|
|
153
|
+
<tbody id="users-tbody">
|
|
154
|
+
<tr><td colspan="6" class="text-center">טוען...</td></tr>
|
|
155
|
+
</tbody>
|
|
156
|
+
</table>
|
|
157
|
+
</div>
|
|
158
|
+
<nav aria-label="ניווט עמודים" class="text-center"><ul class="pagination" id="users-pagination"></ul></nav>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|