feat: add date and multiselect custom fields

This commit is contained in:
Barış Soner Uşaklı
2024-11-20 11:01:01 -05:00
parent e375038ce0
commit 9cf85cede3
8 changed files with 44 additions and 8 deletions

View File

@@ -12,7 +12,9 @@
"input-type-text": "Input (Text)", "input-type-text": "Input (Text)",
"input-type-link": "Input (Link)", "input-type-link": "Input (Link)",
"input-type-number": "Input (Number)", "input-type-number": "Input (Number)",
"input-type-date": "Input (Date)",
"input-type-select": "Select", "input-type-select": "Select",
"input-type-select-multi": "Select Multiple",
"select-options": "Options", "select-options": "Options",
"select-options-help": "Add one option per line for the select element", "select-options-help": "Add one option per line for the select element",
"minimum-reputation": "Minimum reputation", "minimum-reputation": "Minimum reputation",

View File

@@ -213,6 +213,7 @@
"custom-user-field-select-value-invalid": "Custom field selected option is invalid, %1", "custom-user-field-select-value-invalid": "Custom field selected option is invalid, %1",
"custom-user-field-invalid-link": "Custom field link is invalid, %1", "custom-user-field-invalid-link": "Custom field link is invalid, %1",
"custom-user-field-invalid-number": "Custom field number is invalid, %1", "custom-user-field-invalid-number": "Custom field number is invalid, %1",
"custom-user-field-invalid-date": "Custom field date is invalid, %1",
"invalid-custom-user-field": "Invalid custom user field, \"%1\" is already used by NodeBB", "invalid-custom-user-field": "Invalid custom user field, \"%1\" is already used by NodeBB",
"post-already-flagged": "You have already flagged this post", "post-already-flagged": "You have already flagged this post",
"user-already-flagged": "You have already flagged this user", "user-already-flagged": "You have already flagged this user",

View File

@@ -69,7 +69,7 @@ define('admin/manage/user/custom-fields', [
label: '[[global:save]]', label: '[[global:save]]',
callback: function () { callback: function () {
const formData = modal.find('form').serializeObject(); const formData = modal.find('form').serializeObject();
if (formData.type === 'select') { if (formData.type === 'select' || formData.type === 'select-multi') {
formData.selectOptionsFormatted = formData['select-options'].trim().split('\n').join(', '); formData.selectOptionsFormatted = formData['select-options'].trim().split('\n').join(', ');
} }
@@ -91,7 +91,7 @@ define('admin/manage/user/custom-fields', [
modal.find('#type-select').on('change', function () { modal.find('#type-select').on('change', function () {
const type = $(this).val(); const type = $(this).val();
modal.find(`[data-input-type]`).addClass('hidden'); modal.find(`[data-input-type]`).addClass('hidden');
modal.find(`[data-input-type="${type}"]`).removeClass('hidden'); modal.find(`[data-input-type-${type}]`).removeClass('hidden');
}); });
modal.find('#icon-select').on('click', function () { modal.find('#icon-select').on('click', function () {

View File

@@ -46,8 +46,18 @@ define('forum/account/edit', [
const els = $('[component="group/badge/list"] [component="group/badge/item"][data-selected="true"]'); const els = $('[component="group/badge/list"] [component="group/badge/item"][data-selected="true"]');
return els.map((i, el) => $(el).attr('data-value')).get(); return els.map((i, el) => $(el).attr('data-value')).get();
} }
const editForm = $('form[component="profile/edit/form"]');
const userData = editForm.serializeObject();
// stringify multi selects
editForm.find('select[multiple]').each((i, el) => {
const name = $(el).attr('name');
if (userData[name] && !Array.isArray(userData[name])) {
userData[name] = [userData[name]];
}
userData[name] = JSON.stringify(userData[name] || []);
});
const userData = $('form[component="profile/edit/form"]').serializeObject();
userData.uid = ajaxify.data.uid; userData.uid = ajaxify.data.uid;
userData.groupTitle = userData.groupTitle || ''; userData.groupTitle = userData.groupTitle || '';
userData.groupTitle = JSON.stringify(getGroupSelection()); userData.groupTitle = JSON.stringify(getGroupSelection());

View File

@@ -144,14 +144,23 @@ helpers.getCustomUserFields = async function (userData) {
}); });
fields.forEach((f) => { fields.forEach((f) => {
let userValue = userData[f.key];
if (f.type === 'select-multi' && userValue) {
userValue = JSON.parse(userValue || '[]');
}
f['select-options'] = f['select-options'].split('\n').filter(Boolean).map( f['select-options'] = f['select-options'].split('\n').filter(Boolean).map(
opt => ({ opt => ({
value: opt, value: opt,
selected: opt === userData[f.key], selected: Array.isArray(userValue) ?
userValue.includes(opt) :
opt === userValue,
}) })
); );
if (userData[f.key]) { if (userValue) {
f.value = validator.escape(String(userData[f.key])); if (Array.isArray(userValue)) {
userValue = userValue.join(', ');
}
f.value = validator.escape(String(userValue));
} }
}); });
return fields; return fields;

View File

@@ -113,6 +113,10 @@ module.exports = function (User) {
throw new Error(tx.compile( throw new Error(tx.compile(
'error:custom-user-field-invalid-number', field.name 'error:custom-user-field-invalid-number', field.name
)); ));
} else if (value && type === 'input-date' && !validator.isDate(value)) {
throw new Error(tx.compile(
'error:custom-user-field-invalid-date', field.name
));
} else if (value && field.type === 'input-link' && !validator.isURL(String(value))) { } else if (value && field.type === 'input-link' && !validator.isURL(String(value))) {
throw new Error(tx.compile( throw new Error(tx.compile(
'error:custom-user-field-invalid-link', field.name 'error:custom-user-field-invalid-link', field.name
@@ -124,6 +128,14 @@ module.exports = function (User) {
'error:custom-user-field-select-value-invalid', field.name 'error:custom-user-field-select-value-invalid', field.name
)); ));
} }
} else if (field.type === 'select-multi') {
const opts = field['select-options'].split('\n').filter(Boolean);
const values = JSON.parse(value || '[]');
if (!Array.isArray(values) || !values.every(value => opts.includes(value))) {
throw new Error(tx.compile(
'error:custom-user-field-select-value-invalid', field.name
));
}
} }
} }
}); });

View File

@@ -35,7 +35,7 @@
<td class="text-nowrap">{{{ if ./icon }}}<i class="text-muted {./icon}"></i> {{{ end }}}{./name}</td> <td class="text-nowrap">{{{ if ./icon }}}<i class="text-muted {./icon}"></i> {{{ end }}}{./name}</td>
<td> <td>
{./type} {./type}
{{{ if (./type == "select") }}} {{{ if ((./type == "select") || (./type == "select-multi")) }}}
<div class="text-muted"> <div class="text-muted">
({./selectOptionsFormatted}) ({./selectOptionsFormatted})
</div> </div>

View File

@@ -5,7 +5,9 @@
<option value="input-text" {{{ if (type == "input-text") }}}selected{{{ end }}}>[[admin/manage/user-custom-fields:input-type-text]]</option> <option value="input-text" {{{ if (type == "input-text") }}}selected{{{ end }}}>[[admin/manage/user-custom-fields:input-type-text]]</option>
<option value="input-link" {{{ if (type == "input-link") }}}selected{{{ end }}}>[[admin/manage/user-custom-fields:input-type-link]]</option> <option value="input-link" {{{ if (type == "input-link") }}}selected{{{ end }}}>[[admin/manage/user-custom-fields:input-type-link]]</option>
<option value="input-number" {{{ if (type == "input-number") }}}selected{{{ end }}}>[[admin/manage/user-custom-fields:input-type-number]]</option> <option value="input-number" {{{ if (type == "input-number") }}}selected{{{ end }}}>[[admin/manage/user-custom-fields:input-type-number]]</option>
<option value="input-date" {{{ if (type == "input-date") }}}selected{{{ end }}}>[[admin/manage/user-custom-fields:input-type-date]]</option>
<option value="select" {{{ if (type == "select") }}}selected{{{ end }}}>[[admin/manage/user-custom-fields:input-type-select]]</option> <option value="select" {{{ if (type == "select") }}}selected{{{ end }}}>[[admin/manage/user-custom-fields:input-type-select]]</option>
<option value="select-multi" {{{ if (type == "select-multi") }}}selected{{{ end }}}>[[admin/manage/user-custom-fields:input-type-select-multi]]</option>
</select> </select>
</div> </div>
@@ -33,7 +35,7 @@
<p class="form-text">[[admin/manage/user-custom-fields:minimum-reputation-help]]</p> <p class="form-text">[[admin/manage/user-custom-fields:minimum-reputation-help]]</p>
</div> </div>
<div class="mb-3 {{{ if (type != "select") }}}hidden{{{ end }}}" data-input-type="select"> <div class="mb-3 {{{ if ((type != "select") && (type != "select-multi")) }}}hidden{{{ end }}}" data-input-type data-input-type-select data-input-type-select-multi>
<label class="form-label">[[admin/manage/user-custom-fields:select-options]]</label> <label class="form-label">[[admin/manage/user-custom-fields:select-options]]</label>
<textarea class="form-control" name="select-options" rows="6">{./select-options}</textarea> <textarea class="form-control" name="select-options" rows="6">{./select-options}</textarea>
<p class="form-text">[[admin/manage/user-custom-fields:select-options-help]]</p> <p class="form-text">[[admin/manage/user-custom-fields:select-options-help]]</p>