PluginProbe
MainWP Dashboard: Self-hosted WordPress Management for Agencies / 6.3
MainWP Dashboard: Self-hosted WordPress Management for Agencies v6.3
6.3 6.2.3 6.2.2 6.2.1 6.2 6.1.8 6.1.7 6.1.6 6.1.5 6.1.4 6.1.3 6.1.2 6.1.1 6.1 6.0.12 6.0.11 4.6.0.1 5.0 5.0.1 5.0.2 5.0.3 5.0.3.1 5.0.3.2 5.1 5.1.1 All 157 releases
mainwp / assets / js / mainwp-knowledge.js

mainwp-knowledge.js in MainWP Dashboard: Self-hosted WordPress Management for Agencies 6.3, at assets/js/mainwp-knowledge.js

725 lines 35.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /* global jQuery, ajaxurl, mainwp_secure_data, mainwp_confirm */
2
3 /**
4 * Each panel owns its list; a single editor owns the active record request.
5 */
6 jQuery(function ($) {
7 const modal = $('#mainwp-knowledge-modal');
8 if (!modal.length) {
9 return;
10 }
11 const modalHeader = modal.find('.header');
12 const modalTitle = $('#mainwp-knowledge-modal-title');
13 const modalLevel = $('#mainwp-knowledge-modal-level');
14 const modalUnverified = $('#mainwp-knowledge-modal-unverified');
15 const modalMessage = $('#mainwp-knowledge-modal-message');
16 const typeField = $('#mainwp-knowledge-type');
17 const typeViewField = $('#mainwp-knowledge-type-view');
18 const titleField = $('#mainwp-knowledge-title');
19 const descriptionField = $('#mainwp-knowledge-description');
20 const bodyField = $('#mainwp-knowledge-body');
21 const requiredField = $('#mainwp-knowledge-required');
22 const requiredHelp = $('#mainwp-knowledge-required-help');
23 const idField = $('#mainwp-knowledge-id');
24 const revisionField = $('#mainwp-knowledge-revision');
25 const saveButton = $('#mainwp-knowledge-save');
26 const deleteButton = $('#mainwp-knowledge-remove');
27 const cancelButton = modal.find('.actions .cancel.button');
28 const strings = window.mainwpKnowledgeStrings || {};
29 let readOnly = true;
30 let canWrite = false;
31 let scope = null;
32 let owner = null;
33 let session = 0;
34
35 const showMessage = (target, text, color) => {
36 target.removeClass('hidden red green yellow').addClass(color).empty().append($('<p></p>').text(text));
37 };
38 const hideMessage = (target) => target.addClass('hidden').removeClass('red green yellow').empty();
39 const post = (action, data) => $.post(ajaxurl, mainwp_secure_data($.extend({ action: action }, data)), null, 'json');
40 const errorText = (response) => (response && response.data && response.data.message) ? response.data.message : strings.failed;
41 const recordScope = (record) => {
42 const result = { scope_type: record.scope_type };
43 // Agency inputs must omit scope_id even though stored records carry zero.
44 if ('agency' !== record.scope_type) {
45 result.scope_id = parseInt(record.scope_id, 10);
46 }
47 return result;
48 };
49 const updateFields = () => {
50 const isSkill = 'skill' === typeField.val();
51 descriptionField.closest('.field').toggle(isSkill).toggleClass('required', isSkill && !readOnly);
52 descriptionField.prop('required', isSkill && !readOnly).prop('readonly', readOnly);
53 $('#mainwp-knowledge-description-help').toggle(!readOnly);
54 const eligible = scope && ['agency', 'client'].includes(scope.scope_type) && ['context', 'skill'].includes(typeField.val());
55 $('#mainwp-knowledge-required-field').toggle(!!eligible && canWrite && !readOnly);
56 requiredHelp.toggle(!!eligible && canWrite && !readOnly);
57 requiredField.prop('disabled', !eligible || !canWrite || readOnly);
58 };
59 const fill = (record) => {
60 idField.val(record.id);
61 revisionField.val(record.revision);
62 typeField.val(record.type);
63 typeField.closest('.dropdown').dropdown('set selected', record.type);
64 typeViewField.val(record.type_label || '');
65 titleField.val(record.title);
66 descriptionField.val(record.description || '');
67 bodyField.val(record.body);
68 requiredField.prop('checked', !!record.required);
69 modalLevel.text(record.level_label || '').toggle(!!record.level_label);
70 modalUnverified.toggle(record.verified === 0);
71 updateFields();
72 };
73 const openModal = (mode) => {
74 readOnly = 'view' === mode;
75 hideMessage(modalMessage);
76 titleField.prop('readonly', readOnly);
77 bodyField.prop('readonly', readOnly);
78 typeField.closest('.field').toggle(!readOnly);
79 typeViewField.closest('.field').toggle(readOnly);
80 updateFields();
81 saveButton.toggle(!readOnly).prop('disabled', readOnly).toggleClass('disabled', readOnly).removeClass('loading');
82 deleteButton.toggle(!readOnly && parseInt(idField.val(), 10) > 0).prop('disabled', readOnly).removeClass('loading disabled');
83 cancelButton.text(cancelButton.data(readOnly ? 'close' : 'cancel'));
84 modalTitle.text(modalHeader.data(mode));
85 modal.modal('show');
86 };
87 const begin = (nextOwner) => {
88 ++session;
89 owner = nextOwner;
90 scope = null;
91 canWrite = false;
92 readOnly = true;
93 fill({ id: 0, revision: 0, type: 'context', title: '', body: '' });
94 titleField.prop('readonly', true);
95 bodyField.prop('readonly', true);
96 saveButton.prop('disabled', true).addClass('disabled');
97 deleteButton.hide();
98 hideMessage(modalMessage);
99 return session;
100 };
101 const applyRecord = (response, mode) => {
102 const record = response.data.record;
103 scope = recordScope(record);
104 canWrite = !!response.data.can_write;
105 fill(record);
106 openModal('view' === mode || !canWrite ? 'view' : 'edit');
107 };
108 const openRecord = (id, mode, nextOwner) => {
109 const current = begin(nextOwner);
110 post('mainwp_knowledge_get', { id: id }).done((response) => {
111 if (current !== session) {
112 return;
113 }
114 if (!response.success) {
115 showMessage(owner.message, errorText(response), 'yellow');
116 owner.refresh(true);
117 return;
118 }
119 applyRecord(response, mode);
120 }).fail(() => {
121 if (current === session) {
122 showMessage(owner.message, strings.failed, 'red');
123 }
124 });
125 };
126 const reloadIntoEditor = () => {
127 const current = session;
128 const button = modalMessage.find('button');
129 if (button.hasClass('disabled')) {
130 return;
131 }
132 button.addClass('loading disabled');
133 post('mainwp_knowledge_get', { id: idField.val() }).done((response) => {
134 if (current !== session) {
135 return;
136 }
137 if (!response.success) {
138 if (response.data && response.data.code === 'mainwp_knowledge_not_found') {
139 const previousOwner = owner;
140 modal.modal('hide');
141 showMessage(previousOwner.message, errorText(response), 'yellow');
142 previousOwner.refresh(true);
143 return;
144 }
145 showConflict(errorText(response));
146 return;
147 }
148 applyRecord(response, 'edit');
149 showMessage(modalMessage, strings.reloaded, 'yellow');
150 }).fail(() => {
151 if (current === session) {
152 showConflict(strings.failed);
153 }
154 });
155 };
156 const showConflict = (message) => {
157 showMessage(modalMessage, message || strings.conflict, 'yellow');
158 modalMessage.append($('<p></p>').append(
159 $('<button type="button" class="ui mini basic button"></button>').text(strings.reload).on('click', reloadIntoEditor)
160 ));
161 };
162 const save = () => {
163 if (readOnly || !canWrite || !scope || saveButton.hasClass('disabled')) {
164 return;
165 }
166 if ('skill' === typeField.val() && !descriptionField[0].reportValidity()) {
167 return;
168 }
169 const current = session;
170 const previousOwner = owner;
171 hideMessage(modalMessage);
172 saveButton.prop('disabled', true).addClass('loading disabled');
173 deleteButton.prop('disabled', true).addClass('disabled');
174 const data = $.extend({}, scope, {
175 type: typeField.val(),
176 title: titleField.val(),
177 description: 'skill' === typeField.val() ? descriptionField.val() : '',
178 body: bodyField.val(),
179 });
180 if (['agency', 'client'].includes(scope.scope_type)) {
181 data.required = ['context', 'skill'].includes(data.type) && requiredField.prop('checked') ? 1 : 0;
182 }
183 const id = parseInt(idField.val(), 10);
184 if (id > 0) {
185 data.id = id;
186 data.revision = revisionField.val();
187 }
188 post('mainwp_knowledge_save', data).done((response) => {
189 if (response.success) {
190 if (current === session) {
191 modal.modal('hide');
192 }
193 previousOwner.refresh();
194 return;
195 }
196 if (current !== session) {
197 return;
198 }
199 if (response.data && response.data.code === 'mainwp_knowledge_conflict') {
200 saveButton.removeClass('loading');
201 showConflict(response.data.message);
202 return;
203 }
204 showMessage(modalMessage, errorText(response), 'red');
205 saveButton.prop('disabled', false).removeClass('loading disabled');
206 deleteButton.prop('disabled', false).removeClass('disabled');
207 if (response.data && response.data.code === 'mainwp_knowledge_not_found') {
208 previousOwner.refresh();
209 }
210 }).fail(() => {
211 if (current === session) {
212 showMessage(modalMessage, strings.failed, 'red');
213 saveButton.prop('disabled', false).removeClass('loading disabled');
214 deleteButton.prop('disabled', false).removeClass('disabled');
215 }
216 });
217 };
218
219 const reviewLevel = (record, classes) => {
220 const level = record.level_url ? $('<a></a>').attr('href', record.level_url) : $('<span></span>');
221 if (record.level_url) {
222 level.on('click keydown', (event) => event.stopPropagation());
223 }
224 return level.addClass(classes).text(record.level_label || '');
225 };
226 const recordPair = (record, canEdit = false, siteUrl = '', options = {}) => {
227 const recordCanEdit = options.review ? !!record.can_write : canEdit;
228 const title = $('<div class="title mainwp-knowledge-record-title"></div>')
229 .attr('data-id', record.id)
230 .attr('data-revision', record.revision)
231 .data('can-edit', recordCanEdit)
232 .attr({ role: 'button', tabindex: '0', 'aria-expanded': 'false' });
233 const requiredEligible = ['agency', 'client'].includes(record.scope_type) && ['context', 'skill'].includes(record.type);
234 if (requiredEligible) {
235 const checkboxId = `mainwp-knowledge-required-record-${record.id}`;
236 const required = $('<span class="mainwp-knowledge-required-indicator"></span>').on('click keydown', (event) => event.stopPropagation());
237 const checkbox = $('<span class="ui checkbox"></span>').toggleClass('disabled', !recordCanEdit);
238 checkbox.append($('<input type="checkbox" class="mainwp-knowledge-required-toggle">').attr('id', checkboxId).prop('checked', !!record.required).prop('disabled', !recordCanEdit));
239 checkbox.append($('<label></label>').attr('for', checkboxId).text(strings.required));
240 title.append(required.append(checkbox));
241 }
242 if (!record.verified) {
243 title.append($('<span class="ui mini yellow label"></span>').attr('data-tooltip', strings.unverifiedHint).attr('data-inverted', '').text(strings.unverified));
244 } else if (options.review) {
245 title.append($('<span class="ui mini green label"></span>').text(strings.verified));
246 }
247 title.append($('<span class="mainwp-knowledge-record-name"></span>').text(record.title));
248 if (options.review && record.level_label) {
249 title.append(reviewLevel(record, 'ui mini basic label mainwp-knowledge-record-level'));
250 }
251 title.append($('<i class="chevron right grey icon mainwp-knowledge-record-chevron"></i>').attr('aria-hidden', 'true'));
252
253 const content = $('<div class="content mainwp-knowledge-record-content"></div>')
254 .attr('data-id', record.id)
255 .attr('data-revision', record.revision)
256 .attr('aria-hidden', 'true')
257 .data('can-edit', !!recordCanEdit)
258 .data('review', !!options.review)
259 .attr('data-site-url', siteUrl || '');
260 content.append($('<div class="ui active centered inline loader"></div>'));
261 return [title, content];
262 };
263 const loadRecordDetails = (content) => {
264 if (content.data('loaded') || content.data('loading')) {
265 return;
266 }
267 content.data('loading', true);
268 post('mainwp_knowledge_get', { id: content.data('id') }).done((response) => {
269 if (!response.success) {
270 content.empty().append($('<div class="ui yellow message"></div>').text(errorText(response)));
271 return;
272 }
273 const record = response.data.record;
274 const details = $('<div class="mainwp-knowledge-record-details"></div>');
275 const recordCards = $('<div class="ui one cards mainwp-knowledge-record-card"></div>');
276 const recordCard = $('<div class="card"></div>');
277 if (content.data('review') && record.level_label) {
278 recordCard.append($('<div class="content mainwp-knowledge-record-heading"></div>')
279 .append($('<div class="ui small header"></div>').append(reviewLevel(record, 'mainwp-knowledge-record-level-link'))));
280 }
281 const bodyContent = $('<div class="content mainwp-knowledge-record-body"></div>')
282 .attr({ role: 'region', tabindex: '0', 'aria-label': strings.body });
283 bodyContent.append($('<div class="description mainwp-knowledge-record-value"></div>').text(record.body || ''));
284 const descriptionContent = $('<div class="extra content mainwp-knowledge-record-description"></div>');
285 if ('skill' === record.type) {
286 descriptionContent
287 .append($('<div class="ui tiny header"></div>').text(strings.description))
288 .append($('<div class="mainwp-knowledge-record-value"></div>').text(record.description || ''));
289 }
290 const footerContent = $('<div class="extra content mainwp-knowledge-record-footer"></div>');
291 const actions = $('<div class="ui two column middle aligned grid mainwp-knowledge-record-actions"></div>');
292 const leftActions = $('<div class="eight wide left aligned column"></div>');
293 const rightActions = $('<div class="eight wide right aligned column"></div>');
294 leftActions.append($('<span class="ui small text mainwp-knowledge-record-meta"></span>').text(`${strings.editedBy} ${record.updated_by_name} ${record.updated_ago}`));
295 const inReadOnlyWidget = !!content.closest('.mainwp-knowledge-read-only-widget').length;
296 const canEdit = !inReadOnlyWidget && !!content.data('can-edit') && !!response.data.can_write;
297 if (canEdit) {
298 if (content.data('review') && !record.verified) {
299 rightActions.append($('<button type="button" class="ui mini green button mainwp-knowledge-verify"></button>').text(strings.verify));
300 }
301 rightActions.append($('<button type="button" class="ui mini basic button mainwp-knowledge-edit"></button>').text(strings.edit));
302 rightActions.append($('<button type="button" class="ui mini basic red button mainwp-knowledge-delete"></button>').text(strings.remove));
303 } else if (!inReadOnlyWidget) {
304 rightActions.append($('<button type="button" class="ui mini basic button mainwp-knowledge-view"></button>').text(strings.view));
305 }
306 const siteUrl = content.attr('data-site-url');
307 if (siteUrl) {
308 rightActions.append($('<a class="ui mini basic button"></a>').attr('href', siteUrl).text(strings.viewOnSite));
309 }
310 footerContent.append(actions.append(leftActions, rightActions));
311 if ('skill' === record.type) {
312 recordCard.append(descriptionContent);
313 }
314 recordCard.append(bodyContent);
315 recordCard.append(footerContent);
316 recordCards.append(recordCard);
317 details.append(recordCards);
318 content.empty().append(details).data('loaded', true);
319 }).fail(() => {
320 content.empty().append($('<div class="ui red message"></div>').text(strings.failed));
321 }).always(() => content.data('loading', false));
322 };
323 const initializeRecordAccordions = (container) => {
324 container.find('.mainwp-knowledge-records').each(function () {
325 const accordion = $(this);
326 accordion.accordion({
327 exclusive: false,
328 onOpening: function () {
329 $(this).attr('aria-hidden', 'false').prev('.mainwp-knowledge-record-title').attr('aria-expanded', 'true');
330 loadRecordDetails($(this));
331 },
332 onClosing: function () {
333 $(this).attr('aria-hidden', 'true').prev('.mainwp-knowledge-record-title').attr('aria-expanded', 'false');
334 },
335 });
336 // Do not let the surrounding Client Edit accordion process this nested accordion click too.
337 accordion.off('click.mainwpKnowledge').on('click.mainwpKnowledge', '.mainwp-knowledge-record-title', (event) => event.stopPropagation());
338 accordion.off('keydown.mainwpKnowledge').on('keydown.mainwpKnowledge', '.mainwp-knowledge-record-title', function (event) {
339 if ($(event.target).is('input, label, button, a')) {
340 return;
341 }
342 if (['Enter', ' '].includes(event.key)) {
343 event.preventDefault();
344 $(this).trigger('click');
345 }
346 });
347 });
348 };
349 const renderRecordSet = (target, records, emptyText, canEdit = false, siteUrl = '', options = {}) => {
350 const placeholder = target.siblings('.mainwp-knowledge-empty');
351 const sectionBox = target.closest('.mainwp-knowledge-section-box');
352 const footer = sectionBox.children('.mainwp-widget-footer');
353 const addButton = sectionBox.find('.mainwp-knowledge-add').first();
354 target.empty();
355 placeholder.hide().attr('aria-hidden', 'true');
356 if (!records.length) {
357 if (placeholder.length && (emptyText === strings.empty || options.usePlaceholder)) {
358 target.hide().attr('aria-hidden', 'true');
359 placeholder.show().attr('aria-hidden', 'false');
360 addButton.appendTo(placeholder.find('.mainwp-empty-widget-placeholder'));
361 footer.hide();
362 } else {
363 target.show().attr('aria-hidden', 'false').append($('<div class="mainwp-knowledge-empty"></div>').append($('<em></em>').text(emptyText)));
364 footer.hide();
365 }
366 } else {
367 target.show().attr('aria-hidden', 'false');
368 addButton.appendTo(footer);
369 footer.toggle(canEdit);
370 records.forEach((record) => {
371 const pair = recordPair(record, canEdit, siteUrl, options);
372 target.append(pair[0], pair[1]);
373 });
374 }
375 initializeRecordAccordions(target.parent());
376 };
377 const recordContainer = (element) => element.closest('[data-id]');
378 const updateRequired = (checkbox, message, refresh) => {
379 const checkboxUi = checkbox.closest('.ui.checkbox');
380 const record = checkbox.closest('.mainwp-knowledge-record-title');
381 const next = checkbox.prop('checked');
382 const restore = () => {
383 checkbox.prop('checked', !next).prop('disabled', false);
384 checkboxUi.removeClass('disabled').removeAttr('aria-busy');
385 };
386 if (!record.data('can-edit') || checkbox.prop('disabled')) {
387 restore();
388 return;
389 }
390 checkbox.prop('disabled', true);
391 checkboxUi.addClass('disabled').attr('aria-busy', 'true');
392 post('mainwp_knowledge_required', { id: record.data('id'), revision: record.data('revision'), required: next ? 1 : 0 }).done((response) => {
393 if (response.success) {
394 checkbox.prop('disabled', false);
395 checkboxUi.removeClass('disabled').removeAttr('aria-busy');
396 refresh();
397 return;
398 }
399 restore();
400 showMessage(message, errorText(response), 'yellow');
401 if (response.data && ['mainwp_knowledge_conflict', 'mainwp_knowledge_not_found'].includes(response.data.code)) {
402 refresh(true);
403 }
404 }).fail(() => {
405 restore();
406 showMessage(message, strings.failed, 'red');
407 });
408 };
409
410 $('.mainwp-knowledge-panel').each(function () {
411 const panel = $(this);
412 const panelScope = recordScope({ scope_type: panel.data('scope-type'), scope_id: panel.data('scope-id') });
413 const panelReadOnly = '1' === panel.attr('data-read-only') || !!panel.closest('.mainwp-knowledge-read-only-widget').length;
414 if (panelReadOnly) {
415 panelScope.read_only = 1;
416 }
417 const message = panel.find('.mainwp-knowledge-message');
418 const loader = panel.children('.mainwp-knowledge-loader');
419 const inheritedBlock = panel.find('.mainwp-knowledge-inherited');
420 let panelCanWrite = false;
421 const renderInherited = (inherited) => {
422 if (!inherited) {
423 inheritedBlock.hide().find('.mainwp-knowledge-records').empty();
424 return;
425 }
426 inheritedBlock.find('.mainwp-knowledge-inherited-title').text(strings.inheritedFrom.replace('%s', () => inherited.client_name));
427 inheritedBlock.find('.mainwp-knowledge-inherited-link').attr('href', inherited.client_url);
428 inheritedBlock.find('.mainwp-knowledge-inherited-empty').toggle(!inherited.records.length).text(inherited.records.length ? '' : strings.inheritedEmpty);
429 inheritedBlock.find('.mainwp-knowledge-inherited-sections').toggle(!!inherited.records.length);
430 inheritedBlock.find('.mainwp-knowledge-inherited-section').each(function () {
431 const section = $(this);
432 const records = inherited.records.filter((record) => record.type === section.data('type'));
433 renderRecordSet(section.find('.mainwp-knowledge-records'), records, strings.empty);
434 });
435 inheritedBlock.show();
436 };
437 const render = (records, denied) => {
438 panel.find('.mainwp-knowledge-section').each(function () {
439 const section = $(this);
440 const own = records.filter((record) => record.type === section.data('type'));
441 renderRecordSet(section.find('.mainwp-knowledge-records'), own, denied ? strings.noAccess : strings.empty, panelCanWrite);
442 section.find('.mainwp-knowledge-add').toggle(panelCanWrite);
443 });
444 };
445 const load = (preserveMessage = false) => {
446 loader.addClass('active').attr('aria-hidden', 'false');
447 post('mainwp_knowledge_list', panelScope).done((response) => {
448 if (response.success) {
449 panelCanWrite = !panelReadOnly && !!response.data.can_write;
450 if (!preserveMessage) {
451 hideMessage(message);
452 }
453 render(response.data.records, false);
454 renderInherited(response.data.inherited);
455 } else {
456 const code = response.data ? response.data.code : '';
457 if ('mainwp_knowledge_insecure' !== code && response.data && [403, 404].includes(response.data.status)) {
458 panelCanWrite = false;
459 render([], true);
460 renderInherited(null);
461 }
462 showMessage(message, errorText(response), 'yellow');
463 }
464 }).fail(() => showMessage(message, strings.failed, 'red')).always(() => {
465 loader.removeClass('active').attr('aria-hidden', 'true');
466 });
467 };
468 const panelOwner = { message: message, refresh: load };
469 panel.on('change', '.mainwp-knowledge-required-toggle', function () {
470 if (panelReadOnly) {
471 return;
472 }
473 updateRequired($(this), message, load);
474 });
475 panel.on('click', '.mainwp-knowledge-add', function () {
476 if (panelReadOnly || !panelCanWrite) {
477 return;
478 }
479 begin(panelOwner);
480 scope = $.extend({}, panelScope);
481 canWrite = panelCanWrite;
482 fill({ id: 0, revision: 0, type: $(this).data('type'), title: '', description: '', body: '' });
483 openModal('add');
484 });
485 panel.on('click', '.mainwp-knowledge-edit, .mainwp-knowledge-view', function (event) {
486 event.preventDefault();
487 openRecord(recordContainer($(this)).data('id'), panelReadOnly || $(this).hasClass('mainwp-knowledge-view') ? 'view' : 'edit', panelOwner);
488 });
489 panel.on('click', '.mainwp-knowledge-delete', function () {
490 if (panelReadOnly) {
491 return;
492 }
493 const record = recordContainer($(this));
494 mainwp_confirm(strings.confirmDelete, () => {
495 post('mainwp_knowledge_delete', { id: record.data('id'), revision: record.data('revision') }).done((response) => {
496 if (response.success) {
497 load();
498 } else {
499 showMessage(message, errorText(response), 'yellow');
500 load(true);
501 }
502 }).fail(() => showMessage(message, strings.failed, 'red'));
503 });
504 });
505 load();
506 });
507
508 $('.mainwp-knowledge-site-list').each(function () {
509 const list = $(this);
510 const siteScope = recordScope({ scope_type: 'site', scope_id: list.data('scope-id') });
511 const message = list.children('.mainwp-knowledge-message');
512 const loader = list.children('.mainwp-knowledge-loader');
513 const sections = list.find('.mainwp-knowledge-site-sections');
514 const empty = list.find('.mainwp-knowledge-site-empty');
515 let request = 0;
516 const load = (preserveMessage = false) => {
517 const current = ++request;
518 list.find('.mainwp-knowledge-records').empty();
519 loader.addClass('active').attr('aria-hidden', 'false');
520 post('mainwp_knowledge_list', $.extend({}, siteScope, { read_only: 1 })).done((response) => {
521 if (current !== request) {
522 return;
523 }
524 if (!response.success) {
525 showMessage(message, errorText(response), 'yellow');
526 return;
527 }
528 if (!preserveMessage) {
529 hideMessage(message);
530 }
531 const hasRecords = !!response.data.records.length;
532 empty.toggle(!hasRecords).text(hasRecords ? '' : strings.noRecords);
533 sections.toggle(hasRecords);
534 list.find('.mainwp-knowledge-site-section').each(function () {
535 const section = $(this);
536 const records = response.data.records.filter((record) => record.type === section.data('type'));
537 renderRecordSet(section.find('.mainwp-knowledge-records'), records, strings.empty, false, list.data('site-url'));
538 });
539 }).fail(() => {
540 if (current === request) {
541 showMessage(message, strings.failed, 'red');
542 }
543 }).always(() => {
544 if (current === request) {
545 loader.removeClass('active').attr('aria-hidden', 'true');
546 }
547 });
548 };
549 const siteOwner = { message: message, refresh: load };
550 list.data('knowledge-owner', siteOwner);
551 list.on('click', '.mainwp-knowledge-view', function (event) {
552 event.preventDefault();
553 openRecord(recordContainer($(this)).data('id'), 'view', siteOwner);
554 });
555 });
556 $('.mainwp-knowledge-site-accordion').each(function () {
557 const accordion = $(this);
558 accordion.find('tr.title a').on('click', (event) => event.stopPropagation());
559 accordion.accordion({
560 exclusive: false,
561 onOpen: function () {
562 const siteOwner = $(this).find('.mainwp-knowledge-site-list').data('knowledge-owner');
563 if (siteOwner) {
564 siteOwner.refresh();
565 }
566 },
567 });
568 });
569
570 const review = $('#mainwp-knowledge-review');
571 if (review.length) {
572 const message = $('#mainwp-knowledge-review-message');
573 const emptyState = $('#mainwp-knowledge-review-empty');
574 const filteredEmpty = $('#mainwp-knowledge-review-filtered-empty');
575 const sections = review.find('.mainwp-knowledge-review-sections');
576 const loader = review.children('.mainwp-knowledge-loader');
577 const filters = $('#mainwp-knowledge-review-filters');
578 let request = 0;
579 const clearRecords = () => review.find('.mainwp-knowledge-records').empty();
580 const renderRecords = (records) => {
581 review.find('.mainwp-knowledge-review-section').each(function () {
582 const section = $(this);
583 const typeRecords = records.filter((record) => record.type === section.data('type'));
584 renderRecordSet(section.find('.mainwp-knowledge-records'), typeRecords, strings.noRecords, true, '', { review: true, usePlaceholder: true });
585 });
586 };
587 const load = (preserveMessage = false) => {
588 const current = ++request;
589 const data = {
590 draw: current,
591 status: $('#mainwp-knowledge-filter-status').val(),
592 level: $('#mainwp-knowledge-filter-level').val(),
593 };
594 loader.addClass('active').attr('aria-hidden', 'false');
595 post('mainwp_knowledge_review', data).done((response) => {
596 if (current !== request) {
597 return;
598 }
599 if (response.success === false) {
600 showMessage(message, errorText(response), 'yellow');
601 clearRecords();
602 return;
603 }
604 if (!preserveMessage) {
605 hideMessage(message);
606 }
607 const total = Number(response.recordsTotal);
608 const filtered = Number(response.recordsFiltered);
609 const hasRecords = total > 0;
610 const hasFilteredRecords = filtered > 0;
611 filters.toggleClass('hidden', !hasRecords).attr('aria-hidden', hasRecords ? 'false' : 'true');
612 review.toggleClass('hidden', !hasRecords).attr('aria-hidden', hasRecords ? 'false' : 'true');
613 emptyState.toggleClass('hidden', hasRecords).attr('aria-hidden', hasRecords ? 'true' : 'false');
614 sections.toggleClass('hidden', !hasFilteredRecords).attr('aria-hidden', hasFilteredRecords ? 'false' : 'true');
615 filteredEmpty.toggleClass('hidden', !hasRecords || hasFilteredRecords).attr('aria-hidden', hasRecords && !hasFilteredRecords ? 'false' : 'true');
616 clearRecords();
617 if (hasFilteredRecords) {
618 renderRecords(response.data);
619 }
620 }).fail(() => {
621 if (current === request) {
622 showMessage(message, strings.failed, 'red');
623 clearRecords();
624 }
625 }).always(() => {
626 if (current === request) {
627 loader.removeClass('active').attr('aria-hidden', 'true');
628 }
629 });
630 };
631 const reviewOwner = { message: message, refresh: load };
632 filters.find('select').dropdown().on('change', () => load());
633 review.on('change', '.mainwp-knowledge-required-toggle', function () {
634 updateRequired($(this), message, load);
635 });
636 review.on('click', '.mainwp-knowledge-edit, .mainwp-knowledge-view', function (event) {
637 event.preventDefault();
638 openRecord(recordContainer($(this)).data('id'), $(this).hasClass('mainwp-knowledge-view') ? 'view' : 'edit', reviewOwner);
639 });
640 review.on('click', '.mainwp-knowledge-verify', function () {
641 const button = $(this);
642 const record = recordContainer(button);
643 button.prop('disabled', true).addClass('loading disabled');
644 post('mainwp_knowledge_verify', { id: record.data('id'), revision: record.data('revision') }).done((response) => {
645 if (response.success) {
646 load();
647 } else {
648 showMessage(message, errorText(response), 'yellow');
649 load(true);
650 }
651 }).fail(() => {
652 button.prop('disabled', false).removeClass('loading disabled');
653 showMessage(message, strings.failed, 'red');
654 });
655 });
656 review.on('click', '.mainwp-knowledge-delete', function () {
657 const record = recordContainer($(this));
658 mainwp_confirm(strings.confirmDelete, () => {
659 post('mainwp_knowledge_delete', { id: record.data('id'), revision: record.data('revision') }).done((response) => {
660 if (response.success) {
661 load();
662 } else {
663 showMessage(message, errorText(response), 'yellow');
664 load(true);
665 }
666 }).fail(() => showMessage(message, strings.failed, 'red'));
667 });
668 });
669 load();
670 }
671
672 modal.modal({ closable: false, onHide: () => {
673 ++session;
674 owner = null;
675 scope = null;
676 canWrite = false;
677 readOnly = true;
678 } });
679 saveButton.on('click', save);
680 deleteButton.on('click', () => {
681 if (readOnly || !canWrite || deleteButton.hasClass('disabled')) {
682 return;
683 }
684 const current = session;
685 const previousOwner = owner;
686 const data = { id: idField.val(), revision: revisionField.val() };
687 mainwp_confirm(strings.confirmDelete, () => {
688 if (current !== session) {
689 return;
690 }
691 deleteButton.prop('disabled', true).addClass('loading disabled');
692 saveButton.prop('disabled', true).addClass('disabled');
693 post('mainwp_knowledge_delete', data).done((response) => {
694 if (response.success) {
695 if (current === session) {
696 modal.modal('hide');
697 }
698 previousOwner.refresh();
699 return;
700 }
701 if (current !== session) {
702 return;
703 }
704 if (response.data && response.data.code === 'mainwp_knowledge_conflict') {
705 deleteButton.removeClass('loading');
706 showConflict(response.data.message);
707 } else {
708 showMessage(modalMessage, errorText(response), 'red');
709 deleteButton.prop('disabled', false).removeClass('loading disabled');
710 saveButton.prop('disabled', false).removeClass('disabled');
711 }
712 }).fail(() => {
713 if (current === session) {
714 showMessage(modalMessage, strings.failed, 'red');
715 deleteButton.prop('disabled', false).removeClass('loading disabled');
716 saveButton.prop('disabled', false).removeClass('disabled');
717 }
718 });
719 }, false, false, true);
720 });
721 typeField.on('change', updateFields);
722 typeField.closest('.dropdown').dropdown();
723 requiredField.closest('.checkbox').checkbox();
724 });
725