nodebb-plugin-simple-contact 1.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 -0
- package/package.json +18 -0
- package/plugin.json +20 -0
- package/public/js/admin.js +36 -0
- package/public/js/client.js +54 -0
- package/public/templates/admin/plugins/contact.tpl +78 -0
- package/public/templates/contact.tpl +29 -0
package/library.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const db = require.main.require('./src/database');
|
|
4
|
+
const notifications = require.main.require('./src/notifications');
|
|
5
|
+
const user = require.main.require('./src/user');
|
|
6
|
+
const groups = require.main.require('./src/groups');
|
|
7
|
+
const utils = require.main.require('./src/utils');
|
|
8
|
+
|
|
9
|
+
const ContactPlugin = {};
|
|
10
|
+
|
|
11
|
+
ContactPlugin.init = async function (params) {
|
|
12
|
+
const router = params.router;
|
|
13
|
+
const middleware = params.middleware;
|
|
14
|
+
|
|
15
|
+
console.log('--- Contact Plugin Initialized ---'); // בדיקה שהתוסף נטען
|
|
16
|
+
|
|
17
|
+
// Route for the user facing page
|
|
18
|
+
router.get('/contact', middleware.buildHeader, renderContactPage);
|
|
19
|
+
router.get('/api/contact', renderContactPage);
|
|
20
|
+
|
|
21
|
+
// API Route to submit form
|
|
22
|
+
router.post('/api/contact/send', middleware.applyCSRF, handleContactSubmission);
|
|
23
|
+
|
|
24
|
+
// Route for Admin Panel
|
|
25
|
+
router.get('/admin/plugins/contact', middleware.admin.buildHeader, renderAdminPage);
|
|
26
|
+
router.get('/api/admin/plugins/contact', renderAdminPage);
|
|
27
|
+
|
|
28
|
+
// API to mark as handled
|
|
29
|
+
router.post('/api/admin/plugins/contact/handle', middleware.admin.buildHeader, markAsHandled);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// Render User Page (עם תיקון פירורי הלחם)
|
|
33
|
+
async function renderContactPage(req, res) {
|
|
34
|
+
res.render('contact', {
|
|
35
|
+
title: 'צור קשר',
|
|
36
|
+
breadcrumbs: [
|
|
37
|
+
{ text: 'דף הבית', url: '/' },
|
|
38
|
+
{ text: 'צור קשר' }
|
|
39
|
+
]
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Handle Form Submission (עם הלוגיקה המשופרת והבטוחה)
|
|
44
|
+
async function handleContactSubmission(req, res) {
|
|
45
|
+
const data = req.body;
|
|
46
|
+
|
|
47
|
+
// Validation
|
|
48
|
+
if (!data.fullName || !data.email || !data.content) {
|
|
49
|
+
return res.status(400).json({ error: 'נא למלא את כל שדות החובה.' });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const contactId = Date.now();
|
|
53
|
+
const key = 'contact-request:' + contactId;
|
|
54
|
+
|
|
55
|
+
const contactData = {
|
|
56
|
+
id: contactId,
|
|
57
|
+
fullName: data.fullName,
|
|
58
|
+
username: data.username || 'אורח',
|
|
59
|
+
email: data.email,
|
|
60
|
+
content: data.content,
|
|
61
|
+
timestamp: contactId,
|
|
62
|
+
handled: false
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
// Save to DB
|
|
67
|
+
await db.setObject(key, contactData);
|
|
68
|
+
await db.sortedSetAdd('contact-requests:sorted', contactId, contactId);
|
|
69
|
+
|
|
70
|
+
// Send Notification to Admins
|
|
71
|
+
try {
|
|
72
|
+
const adminUids = await groups.getMembers('administrators', 0, -1);
|
|
73
|
+
|
|
74
|
+
if (adminUids && adminUids.length > 0) {
|
|
75
|
+
const notification = await notifications.create({
|
|
76
|
+
type: 'new-contact',
|
|
77
|
+
bodyShort: `פנייה חדשה מאת ${contactData.fullName}`,
|
|
78
|
+
bodyLong: contactData.content,
|
|
79
|
+
nid: 'contact:' + contactId,
|
|
80
|
+
path: '/admin/plugins/contact',
|
|
81
|
+
from: 0
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
await notifications.push(notification, adminUids);
|
|
85
|
+
} else {
|
|
86
|
+
console.warn('Contact Plugin: No administrators found to notify.');
|
|
87
|
+
}
|
|
88
|
+
} catch (notifyErr) {
|
|
89
|
+
console.error('Contact Plugin Notification Error:', notifyErr);
|
|
90
|
+
// לא מכשילים את הבקשה אם רק ההתראה נכשלה
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
res.json({ success: true, message: 'הפנייה נשלחה בהצלחה.' });
|
|
94
|
+
} catch (err) {
|
|
95
|
+
console.error('Contact Plugin Error:', err);
|
|
96
|
+
res.status(500).json({ error: 'שגיאה פנימית בשליחת הטופס.' });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Render Admin Page
|
|
101
|
+
async function renderAdminPage(req, res) {
|
|
102
|
+
const ids = await db.getSortedSetRevRange('contact-requests:sorted', 0, -1);
|
|
103
|
+
let items = [];
|
|
104
|
+
|
|
105
|
+
if (ids.length > 0) {
|
|
106
|
+
const keys = ids.map(id => 'contact-request:' + id);
|
|
107
|
+
items = await db.getObjects(keys);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
items.forEach(item => {
|
|
111
|
+
// המרת תאריך למחרוזת קריאה
|
|
112
|
+
item.date = new Date(parseInt(item.timestamp)).toLocaleString();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
res.render('admin/plugins/contact', { requests: items });
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Mark as Handled logic
|
|
119
|
+
async function markAsHandled(req, res) {
|
|
120
|
+
const id = req.body.id;
|
|
121
|
+
if (!id) return res.status(400).json({ error: 'Missing ID' });
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
await db.setObjectField('contact-request:' + id, 'handled', true);
|
|
125
|
+
res.json({ success: true });
|
|
126
|
+
} catch (err) {
|
|
127
|
+
res.status(500).json({ error: err.message });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Add link to main navigation
|
|
132
|
+
ContactPlugin.addNavigation = async function (header) {
|
|
133
|
+
header.navigation.push({
|
|
134
|
+
route: '/contact',
|
|
135
|
+
iconClass: 'fa-envelope',
|
|
136
|
+
text: 'צור קשר'
|
|
137
|
+
});
|
|
138
|
+
return header;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// Add link to admin menu
|
|
142
|
+
ContactPlugin.addAdminNavigation = async function (header) {
|
|
143
|
+
header.plugins.push({
|
|
144
|
+
route: '/plugins/contact',
|
|
145
|
+
icon: 'fa-envelope',
|
|
146
|
+
name: 'פניות צור קשר'
|
|
147
|
+
});
|
|
148
|
+
return header;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
module.exports = ContactPlugin;
|
package/package.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nodebb-plugin-simple-contact",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "תוסף טופס צור קשר עם התראות למנהלים וניהול פניות",
|
|
5
|
+
"main": "library.js",
|
|
6
|
+
"nbbpm": {
|
|
7
|
+
"compatibility": "^3.0.0"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [
|
|
10
|
+
"nodebb",
|
|
11
|
+
"plugin",
|
|
12
|
+
"contact",
|
|
13
|
+
"form"
|
|
14
|
+
],
|
|
15
|
+
"author": "Your Name",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"dependencies": {}
|
|
18
|
+
}
|
package/plugin.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "nodebb-plugin-simple-contact",
|
|
3
|
+
"name": "Simple Contact Form",
|
|
4
|
+
"description": "A contact form with admin notifications.",
|
|
5
|
+
"url": "http://localhost",
|
|
6
|
+
"library": "./library.js",
|
|
7
|
+
"hooks": [
|
|
8
|
+
{ "hook": "static:app.load", "method": "init" },
|
|
9
|
+
{ "hook": "filter:navigation.available", "method": "addNavigation" },
|
|
10
|
+
{ "hook": "filter:admin.header.build", "method": "addAdminNavigation" }
|
|
11
|
+
],
|
|
12
|
+
"scripts": [
|
|
13
|
+
"public/js/client.js"
|
|
14
|
+
],
|
|
15
|
+
"acpScripts": [
|
|
16
|
+
"public/js/admin.js"
|
|
17
|
+
],
|
|
18
|
+
"modules": {},
|
|
19
|
+
"templates": "public/templates"
|
|
20
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/* globals $, app, socket */
|
|
4
|
+
|
|
5
|
+
// הקוד הזה ירוץ ברגע שפאנל הניהול נטען
|
|
6
|
+
$(document).ready(function () {
|
|
7
|
+
// האזנה לאירוע ש-NodeBB מסיים לטעון דף חדש (Ajaxify)
|
|
8
|
+
$(window).on('action:ajaxify.end', function (event, data) {
|
|
9
|
+
// בדיקה: האם הגענו לדף של התוסף שלנו?
|
|
10
|
+
if (data.tpl_url === 'admin/plugins/contact') {
|
|
11
|
+
initializeAdminPage();
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// אם אנחנו כבר בדף הזה ברגע הטעינה הראשונה
|
|
16
|
+
if (ajaxify.data.template && ajaxify.data.template['admin/plugins/contact']) {
|
|
17
|
+
initializeAdminPage();
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
function initializeAdminPage() {
|
|
22
|
+
console.log('✅ Contact Admin Page logic triggered via Hook');
|
|
23
|
+
|
|
24
|
+
// לוגיקה לסימון כטופל
|
|
25
|
+
$('.mark-handled-btn').on('click', function () {
|
|
26
|
+
var id = $(this).data('id');
|
|
27
|
+
var btn = $(this);
|
|
28
|
+
|
|
29
|
+
$.post('/api/admin/plugins/contact/handle', { id: id }, function (data) {
|
|
30
|
+
if (data.success) {
|
|
31
|
+
btn.closest('tr').addClass('table-success');
|
|
32
|
+
btn.replaceWith('<span class="badge bg-success">טופל</span>');
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// החלק הזה מטפל בלוגיקה של הטופס
|
|
4
|
+
$(document).ready(function() {
|
|
5
|
+
$(window).on('action:ajaxify.end', function(event, data) {
|
|
6
|
+
if (data.url === 'contact' || ajaxify.data.template.name === 'contact') {
|
|
7
|
+
$('#contact-form').off('submit').on('submit', function(e) {
|
|
8
|
+
e.preventDefault();
|
|
9
|
+
var btn = $('#submit-btn');
|
|
10
|
+
var alertBox = $('#contact-alert');
|
|
11
|
+
var form = $(this);
|
|
12
|
+
|
|
13
|
+
btn.prop('disabled', true);
|
|
14
|
+
|
|
15
|
+
var formData = {
|
|
16
|
+
fullName: form.find('#fullName').val(),
|
|
17
|
+
username: form.find('#username').val(),
|
|
18
|
+
email: form.find('#email').val(),
|
|
19
|
+
content: form.find('#content').val()
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
$.ajax({
|
|
23
|
+
url: config.relative_path + '/api/contact/send',
|
|
24
|
+
type: 'POST',
|
|
25
|
+
data: formData,
|
|
26
|
+
headers: { 'x-csrf-token': config.csrf_token },
|
|
27
|
+
success: function(response) {
|
|
28
|
+
alertBox.removeClass('alert-danger').addClass('alert-success')
|
|
29
|
+
.text(response.message).show();
|
|
30
|
+
form[0].reset();
|
|
31
|
+
},
|
|
32
|
+
error: function(xhr) {
|
|
33
|
+
// הצגת הודעת שגיאה מפורטת אם השרת החזיר כזו
|
|
34
|
+
var msg = (xhr.responseJSON && xhr.responseJSON.error) ? xhr.responseJSON.error : 'שגיאה בשליחה';
|
|
35
|
+
alertBox.removeClass('alert-success').addClass('alert-danger')
|
|
36
|
+
.text(msg).show();
|
|
37
|
+
},
|
|
38
|
+
complete: function() {
|
|
39
|
+
btn.prop('disabled', false);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// החלק הזה "מרגיע" את NodeBB שלא יחפש מודול חסר
|
|
48
|
+
define('forum/contact', [], function() {
|
|
49
|
+
return {
|
|
50
|
+
init: function() {
|
|
51
|
+
console.log('Contact page loaded.');
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<div class="row">
|
|
2
|
+
<div class="col-xs-12">
|
|
3
|
+
|
|
4
|
+
<div class="panel panel-warning">
|
|
5
|
+
<div class="panel-heading">ממתין - פניות לטיפול</div>
|
|
6
|
+
<div class="panel-body">
|
|
7
|
+
<table class="table table-striped">
|
|
8
|
+
<thead>
|
|
9
|
+
<tr>
|
|
10
|
+
<th>תאריך</th>
|
|
11
|
+
<th>שם מלא</th>
|
|
12
|
+
<th>משתמש</th>
|
|
13
|
+
<th>מייל</th>
|
|
14
|
+
<th>תוכן</th>
|
|
15
|
+
<th>סטטוס</th>
|
|
16
|
+
<th>פעולות</th>
|
|
17
|
+
</tr>
|
|
18
|
+
</thead>
|
|
19
|
+
<tbody id="contact-list-waiting">
|
|
20
|
+
{{{ each requests }}}
|
|
21
|
+
{{{ if !./handled }}}
|
|
22
|
+
<tr data-id="{./id}">
|
|
23
|
+
<td>{./date}</td>
|
|
24
|
+
<td>{./fullName}</td>
|
|
25
|
+
<td>{./username}</td>
|
|
26
|
+
<td>{./email}</td>
|
|
27
|
+
<td>{./content}</td>
|
|
28
|
+
<td><span class="label label-warning">ממתין</span></td>
|
|
29
|
+
<td>
|
|
30
|
+
<button class="btn btn-sm btn-primary mark-handled">סמן כטופל</button>
|
|
31
|
+
</td>
|
|
32
|
+
</tr>
|
|
33
|
+
{{{ end }}}
|
|
34
|
+
{{{ end }}}
|
|
35
|
+
</tbody>
|
|
36
|
+
</table>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<hr>
|
|
41
|
+
|
|
42
|
+
<div class="panel panel-success">
|
|
43
|
+
<div class="panel-heading">מטופל - ארכיון פניות</div>
|
|
44
|
+
<div class="panel-body">
|
|
45
|
+
<table class="table table-striped">
|
|
46
|
+
<thead>
|
|
47
|
+
<tr>
|
|
48
|
+
<th>תאריך</th>
|
|
49
|
+
<th>שם מלא</th>
|
|
50
|
+
<th>משתמש</th>
|
|
51
|
+
<th>מייל</th>
|
|
52
|
+
<th>תוכן</th>
|
|
53
|
+
<th>סטטוס</th>
|
|
54
|
+
<th>פעולות</th>
|
|
55
|
+
</tr>
|
|
56
|
+
</thead>
|
|
57
|
+
<tbody id="contact-list-handled">
|
|
58
|
+
{{{ each requests }}}
|
|
59
|
+
{{{ if ./handled }}}
|
|
60
|
+
<tr data-id="{./id}" class="success">
|
|
61
|
+
<td>{./date}</td>
|
|
62
|
+
<td>{./fullName}</td>
|
|
63
|
+
<td>{./username}</td>
|
|
64
|
+
<td>{./email}</td>
|
|
65
|
+
<td>{./content}</td>
|
|
66
|
+
<td><span class="label label-success">טופל</span></td>
|
|
67
|
+
<td>
|
|
68
|
+
</td>
|
|
69
|
+
</tr>
|
|
70
|
+
{{{ end }}}
|
|
71
|
+
{{{ end }}}
|
|
72
|
+
</tbody>
|
|
73
|
+
</table>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<div class="row">
|
|
2
|
+
<div class="col-lg-8 col-lg-offset-2">
|
|
3
|
+
<div class="panel panel-default">
|
|
4
|
+
<div class="panel-heading"><h3 class="panel-title">צור קשר עם ההנהלה</h3></div>
|
|
5
|
+
<div class="panel-body">
|
|
6
|
+
<form id="contact-form" role="form">
|
|
7
|
+
<div class="form-group">
|
|
8
|
+
<label for="fullName">שם מלא *</label>
|
|
9
|
+
<input type="text" class="form-control" id="fullName" name="fullName" required>
|
|
10
|
+
</div>
|
|
11
|
+
<div class="form-group">
|
|
12
|
+
<label for="username">שם משתמש בפורום (במידה וקיים)</label>
|
|
13
|
+
<input type="text" class="form-control" id="username" name="username">
|
|
14
|
+
</div>
|
|
15
|
+
<div class="form-group">
|
|
16
|
+
<label for="email">כתובת מייל *</label>
|
|
17
|
+
<input type="email" class="form-control" id="email" name="email" required>
|
|
18
|
+
</div>
|
|
19
|
+
<div class="form-group">
|
|
20
|
+
<label for="content">תוכן הפנייה *</label>
|
|
21
|
+
<textarea class="form-control" id="content" name="content" rows="6" required></textarea>
|
|
22
|
+
</div>
|
|
23
|
+
<button type="submit" class="btn btn-primary" id="submit-btn">שלח פנייה</button>
|
|
24
|
+
</form>
|
|
25
|
+
<div id="contact-alert" class="alert" style="display:none; margin-top: 20px;"></div>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|