admin.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. /**
  2. * Admin JavaScript for QDR Temporary Shared Storage plugin
  3. *
  4. * Handles all admin-side functionality including:
  5. * - Media page (loading, pagination, sorting, filtering, editing, deleting)
  6. * - Settings page (API key generation, settings saving, purging expired files)
  7. *
  8. * @package QDR_Temporary_Shared_Storage
  9. * @since 1.0.0
  10. */
  11. (function($) {
  12. 'use strict';
  13. // Media page functionality
  14. var QDR_TSS_Media = {
  15. currentPage: 1,
  16. perPage: 10,
  17. totalPages: 1,
  18. currentOrderBy: 'upload_date',
  19. currentOrder: 'DESC',
  20. currentSearchFilename: '',
  21. currentSearchReference: '',
  22. /**
  23. * Initialize the media page
  24. */
  25. init: function() {
  26. // Load items on page load
  27. this.loadItems();
  28. // Initialize event handlers
  29. this.initEventHandlers();
  30. // Initialize datepicker
  31. this.initDatepicker();
  32. },
  33. /**
  34. * Initialize event handlers
  35. */
  36. initEventHandlers: function() {
  37. var self = this;
  38. // Handle sorting
  39. $('.qdr-tss-table th.sortable').on('click', function() {
  40. var column = $(this).data('sort');
  41. if (self.currentOrderBy === column) {
  42. self.currentOrder = self.currentOrder === 'ASC' ? 'DESC' : 'ASC';
  43. } else {
  44. self.currentOrderBy = column;
  45. self.currentOrder = 'ASC';
  46. }
  47. self.loadItems();
  48. });
  49. // Handle search
  50. $('#qdr-tss-search-button').on('click', function() {
  51. self.currentSearchFilename = $('#qdr-tss-search-filename').val();
  52. self.currentSearchReference = $('#qdr-tss-search-reference').val();
  53. self.currentPage = 1;
  54. self.loadItems();
  55. });
  56. // Handle search reset
  57. $('#qdr-tss-reset-search').on('click', function() {
  58. $('#qdr-tss-search-filename').val('');
  59. $('#qdr-tss-search-reference').val('');
  60. self.currentSearchFilename = '';
  61. self.currentSearchReference = '';
  62. self.currentPage = 1;
  63. self.loadItems();
  64. });
  65. // Handle refresh button
  66. $('#qdr-tss-refresh').on('click', function() {
  67. self.loadItems();
  68. });
  69. // Handle modal close
  70. $('.qdr-tss-modal-close, .qdr-tss-modal-cancel').on('click', function() {
  71. $('.qdr-tss-modal').hide();
  72. });
  73. // Close modal when clicking outside
  74. $(window).on('click', function(e) {
  75. if ($(e.target).hasClass('qdr-tss-modal')) {
  76. $('.qdr-tss-modal').hide();
  77. }
  78. });
  79. // Handle copy shortcode
  80. $(document).on('click', '.qdr-tss-copy-shortcode', function() {
  81. var shortcode = $('#qdr-tss-details-shortcode code').text();
  82. self.copyToClipboard(shortcode);
  83. alert(qdr_tss.strings.shortcode_copied);
  84. });
  85. // Handle copy permalink
  86. $(document).on('click', '.qdr-tss-copy-permalink', function() {
  87. var permalink = $('#qdr-tss-details-permalink a').attr('href');
  88. self.copyToClipboard(permalink);
  89. alert(qdr_tss.strings.permalink_copied);
  90. });
  91. // Handle edit item form submission
  92. $('#qdr-tss-edit-form').on('submit', function(e) {
  93. e.preventDefault();
  94. self.updateItem();
  95. });
  96. // Handle confirm delete
  97. $(document).on('click', '.qdr-tss-confirm-delete-button', function() {
  98. var id = $('#qdr-tss-delete-id').val();
  99. self.deleteItem(id);
  100. });
  101. },
  102. /**
  103. * Initialize datepicker
  104. */
  105. initDatepicker: function() {
  106. $(document).on('focus', '.qdr-tss-datepicker', function() {
  107. $(this).datepicker({
  108. dateFormat: 'yy-mm-dd',
  109. changeMonth: true,
  110. changeYear: true
  111. });
  112. });
  113. },
  114. /**
  115. * Initialize action buttons
  116. */
  117. initActionButtons: function() {
  118. var self = this;
  119. // View button
  120. $('.qdr-tss-view-button').off('click').on('click', function() {
  121. var id = $(this).data('id');
  122. self.viewItem(id);
  123. });
  124. // Edit button
  125. $('.qdr-tss-edit-button').off('click').on('click', function() {
  126. var id = $(this).data('id');
  127. self.editItem(id);
  128. });
  129. // Delete button
  130. $('.qdr-tss-delete-button').off('click').on('click', function() {
  131. var id = $(this).data('id');
  132. self.showDeleteConfirmation(id);
  133. });
  134. // Pagination
  135. $('.qdr-tss-page-button').off('click').on('click', function() {
  136. self.currentPage = parseInt($(this).data('page'));
  137. self.loadItems();
  138. });
  139. },
  140. /**
  141. * Load items
  142. */
  143. loadItems: function() {
  144. var self = this;
  145. var data = {
  146. action: 'qdr_tss_get_items',
  147. nonce: qdr_tss.nonce,
  148. page: this.currentPage,
  149. per_page: this.perPage,
  150. orderby: this.currentOrderBy,
  151. order: this.currentOrder,
  152. original_file_name: this.currentSearchFilename,
  153. reference: this.currentSearchReference
  154. };
  155. // Show loading indicator
  156. $('#qdr-tss-items-list').html('<tr><td colspan="11" class="qdr-tss-loading"><span class="spinner is-active"></span> ' + qdr_tss.strings.loading + '</td></tr>');
  157. $.post(ajaxurl, data, function(response) {
  158. if (response.success) {
  159. self.renderItems(response.data.items);
  160. self.renderPagination(response.data.total, response.data.total_pages);
  161. } else {
  162. alert(response.data.message || qdr_tss.strings.error_loading_items);
  163. }
  164. }).fail(function() {
  165. $('#qdr-tss-items-list').html('<tr><td colspan="11">' + qdr_tss.strings.error_loading_items_try_again + '</td></tr>');
  166. });
  167. },
  168. /**
  169. * Render items
  170. */
  171. renderItems: function(items) {
  172. var self = this;
  173. var $tbody = $('#qdr-tss-items-list');
  174. $tbody.empty();
  175. if (!items || items.length === 0) {
  176. $tbody.append('<tr><td colspan="11">' + qdr_tss.strings.no_items_found + '</td></tr>');
  177. return;
  178. }
  179. $.each(items, function(index, item) {
  180. var statusClass = self.getItemStatusClass(item);
  181. var row = '<tr data-id="' + item.id + '" class="' + statusClass + '">';
  182. row += '<td>' + item.fileid + '</td>';
  183. row += '<td>' + self.escapeHtml(item.original_file_name) + '</td>';
  184. row += '<td>' + self.escapeHtml(item.description || '') + '</td>';
  185. row += '<td>' + self.escapeHtml(item.message || '') + '</td>';
  186. row += '<td>' + self.formatDate(item.active_from) + '</td>';
  187. row += '<td>' + self.formatDate(item.active_to) + '</td>';
  188. row += '<td>' + self.escapeHtml(item.reference || '') + '</td>';
  189. row += '<td>' + self.formatDate(item.upload_date) + '</td>';
  190. row += '<td>' + item.download_count + '</td>';
  191. row += '<td>' + (item.last_download ? self.formatDate(item.last_download) : '-') + '</td>';
  192. row += '<td class="column-actions">';
  193. row += '<div class="qdr-tss-action-buttons">';
  194. row += '<button class="button qdr-tss-view-button" data-id="' + item.id + '" title="' + qdr_tss.strings.view_details + '"><span class="dashicons dashicons-visibility"></span></button>';
  195. row += '<button class="button qdr-tss-edit-button" data-id="' + item.id + '" title="' + qdr_tss.strings.edit + '"><span class="dashicons dashicons-edit"></span></button>';
  196. row += '<button class="button qdr-tss-delete-button" data-id="' + item.id + '" title="' + qdr_tss.strings.delete + '"><span class="dashicons dashicons-trash"></span></button>';
  197. row += '</div>';
  198. row += '</td>';
  199. row += '</tr>';
  200. $tbody.append(row);
  201. });
  202. // Initialize action buttons
  203. this.initActionButtons();
  204. // Update sorting indicators
  205. this.updateSortIndicators();
  206. },
  207. /**
  208. * Get item status class
  209. */
  210. getItemStatusClass: function(item) {
  211. var now = new Date();
  212. var activeFrom = new Date(item.active_from);
  213. var activeTo = new Date(item.active_to);
  214. if (now < activeFrom) {
  215. return 'qdr-tss-status-pending';
  216. } else if (now > activeTo) {
  217. return 'qdr-tss-status-expired';
  218. } else {
  219. return 'qdr-tss-status-active';
  220. }
  221. },
  222. /**
  223. * Render pagination
  224. */
  225. renderPagination: function(total, totalPages) {
  226. var $info = $('.qdr-tss-pagination-info');
  227. var $controls = $('.qdr-tss-pagination-controls');
  228. // Update info
  229. var startItem = (this.currentPage - 1) * this.perPage + 1;
  230. var endItem = Math.min(this.currentPage * this.perPage, total);
  231. if (total === 0) {
  232. $info.html(qdr_tss.strings.no_items);
  233. $controls.empty();
  234. return;
  235. }
  236. $info.html(qdr_tss.strings.showing + ' ' + startItem + ' - ' + endItem + ' ' + qdr_tss.strings.of + ' ' + total);
  237. // Update controls
  238. $controls.empty();
  239. if (totalPages <= 1) {
  240. return;
  241. }
  242. // First page
  243. if (this.currentPage > 1) {
  244. $controls.append('<button class="button qdr-tss-page-button" data-page="1" title="' + qdr_tss.strings.first_page + '">&laquo;</button>');
  245. }
  246. // Previous page
  247. if (this.currentPage > 1) {
  248. $controls.append('<button class="button qdr-tss-page-button" data-page="' + (this.currentPage - 1) + '" title="' + qdr_tss.strings.previous_page + '">&lsaquo;</button>');
  249. }
  250. // Page numbers
  251. var startPage = Math.max(1, this.currentPage - 2);
  252. var endPage = Math.min(totalPages, startPage + 4);
  253. if (endPage - startPage < 4) {
  254. startPage = Math.max(1, endPage - 4);
  255. }
  256. for (var i = startPage; i <= endPage; i++) {
  257. var activeClass = i === this.currentPage ? 'button-primary' : '';
  258. $controls.append('<button class="button qdr-tss-page-button ' + activeClass + '" data-page="' + i + '">' + i + '</button>');
  259. }
  260. // Next page
  261. if (this.currentPage < totalPages) {
  262. $controls.append('<button class="button qdr-tss-page-button" data-page="' + (this.currentPage + 1) + '" title="' + qdr_tss.strings.next_page + '">&rsaquo;</button>');
  263. }
  264. // Last page
  265. if (this.currentPage < totalPages) {
  266. $controls.append('<button class="button qdr-tss-page-button" data-page="' + totalPages + '" title="' + qdr_tss.strings.last_page + '">&raquo;</button>');
  267. }
  268. // Rebind click events
  269. this.initActionButtons();
  270. },
  271. /**
  272. * Update sort indicators
  273. */
  274. updateSortIndicators: function() {
  275. // Remove all indicators
  276. $('.qdr-tss-table th').removeClass('sorted asc desc');
  277. // Add indicator to current sort column
  278. var $th = $('.qdr-tss-table th[data-sort="' + this.currentOrderBy + '"]');
  279. $th.addClass('sorted ' + this.currentOrder.toLowerCase());
  280. },
  281. /**
  282. * View item details
  283. */
  284. viewItem: function(id) {
  285. var self = this;
  286. // Show loading modal
  287. $('#qdr-tss-details-modal').show();
  288. $('.qdr-tss-details-content').html('<div class="qdr-tss-loading"><span class="spinner is-active"></span> ' + qdr_tss.strings.loading + '</div>');
  289. // Get additional data via AJAX
  290. var data = {
  291. action: 'qdr_tss_get_item_details',
  292. nonce: qdr_tss.nonce,
  293. id: id
  294. };
  295. $.post(ajaxurl, data, function(response) {
  296. if (response.success) {
  297. var item = response.data.item;
  298. // Populate details
  299. $('#qdr-tss-details-fileid').text(item.fileid);
  300. $('#qdr-tss-details-filename').text(item.original_file_name);
  301. $('#qdr-tss-details-description').text(item.description || '-');
  302. $('#qdr-tss-details-message').text(item.message || '-');
  303. $('#qdr-tss-details-reference').text(item.reference || '-');
  304. $('#qdr-tss-details-active-period').text(self.formatDate(item.active_from) + ' - ' + self.formatDate(item.active_to));
  305. $('#qdr-tss-details-filesize').text(self.formatFileSize(item.file_size));
  306. $('#qdr-tss-details-download-count').text(item.download_count);
  307. $('#qdr-tss-details-last-download').text(item.last_download ? self.formatDate(item.last_download) : '-');
  308. // Permalink and shortcode
  309. $('#qdr-tss-details-permalink').html('<a href="' + item.permalink + '" target="_blank">' + item.permalink + '</a>');
  310. $('#qdr-tss-details-shortcode').html('<code>' + item.shortcode + '</code>');
  311. // Initialize copy buttons
  312. $('.qdr-tss-copy-shortcode').data('text', item.shortcode);
  313. $('.qdr-tss-copy-permalink').data('text', item.permalink);
  314. } else {
  315. alert(response.data.message || qdr_tss.strings.error_loading_item_details);
  316. $('#qdr-tss-details-modal').hide();
  317. }
  318. }).fail(function() {
  319. alert(qdr_tss.strings.error_loading_item_details_try_again);
  320. $('#qdr-tss-details-modal').hide();
  321. });
  322. },
  323. /**
  324. * Edit item
  325. */
  326. editItem: function(id) {
  327. // Find item in the table
  328. var $row = $('tr[data-id="' + id + '"]');
  329. if ($row.length === 0) return;
  330. var $cells = $row.find('td');
  331. // Populate form fields
  332. $('#qdr-tss-edit-id').val(id);
  333. $('#qdr-tss-edit-description').val($cells.eq(2).text());
  334. $('#qdr-tss-edit-message').val($cells.eq(3).text());
  335. // Parse dates for datepicker
  336. var activeFrom = $cells.eq(4).text();
  337. var activeTo = $cells.eq(5).text();
  338. $('#qdr-tss-edit-active-from').val(activeFrom !== '-' ? activeFrom : '');
  339. $('#qdr-tss-edit-active-to').val(activeTo !== '-' ? activeTo : '');
  340. $('#qdr-tss-edit-reference').val($cells.eq(6).text());
  341. // Clear status
  342. $('#qdr-tss-edit-status').html('');
  343. // Show modal
  344. $('#qdr-tss-edit-modal').show();
  345. },
  346. /**
  347. * Update item
  348. */
  349. updateItem: function() {
  350. var self = this;
  351. // Show loading status
  352. $('#qdr-tss-edit-status').html('<div class="qdr-tss-loading"><span class="spinner is-active"></span> ' + qdr_tss.strings.saving + '</div>');
  353. var data = {
  354. action: 'qdr_tss_edit_item',
  355. nonce: qdr_tss.nonce,
  356. id: $('#qdr-tss-edit-id').val(),
  357. description: $('#qdr-tss-edit-description').val(),
  358. message: $('#qdr-tss-edit-message').val(),
  359. active_from: $('#qdr-tss-edit-active-from').val(),
  360. active_to: $('#qdr-tss-edit-active-to').val(),
  361. reference: $('#qdr-tss-edit-reference').val()
  362. };
  363. $.post(ajaxurl, data, function(response) {
  364. if (response.success) {
  365. // Show success status
  366. $('#qdr-tss-edit-status').html('<div class="notice notice-success inline"><p>' + (response.data.message || qdr_tss.strings.changes_saved) + '</p></div>');
  367. // Hide modal after a delay
  368. setTimeout(function() {
  369. $('#qdr-tss-edit-modal').hide();
  370. // Reload items
  371. self.loadItems();
  372. }, 1000);
  373. } else {
  374. // Show error status
  375. $('#qdr-tss-edit-status').html('<div class="notice notice-error inline"><p>' + (response.data.message || qdr_tss.strings.error_saving_changes) + '</p></div>');
  376. }
  377. }).fail(function() {
  378. // Show error status
  379. $('#qdr-tss-edit-status').html('<div class="notice notice-error inline"><p>' + qdr_tss.strings.error_saving_changes_try_again + '</p></div>');
  380. });
  381. },
  382. /**
  383. * Show delete confirmation
  384. */
  385. showDeleteConfirmation: function(id) {
  386. $('#qdr-tss-delete-id').val(id);
  387. $('#qdr-tss-confirm-delete-modal').show();
  388. },
  389. /**
  390. * Delete item
  391. */
  392. deleteItem: function(id) {
  393. var self = this;
  394. var data = {
  395. action: 'qdr_tss_delete_item',
  396. nonce: qdr_tss.nonce,
  397. id: id
  398. };
  399. $.post(ajaxurl, data, function(response) {
  400. if (response.success) {
  401. // Hide modal
  402. $('#qdr-tss-confirm-delete-modal').hide();
  403. // Show success message
  404. alert(response.data.message || qdr_tss.strings.file_deleted);
  405. // Reload items
  406. self.loadItems();
  407. } else {
  408. alert(response.data.message || qdr_tss.strings.error_deleting_file);
  409. }
  410. }).fail(function() {
  411. alert(qdr_tss.strings.error_deleting_file_try_again);
  412. });
  413. },
  414. /**
  415. * Format date
  416. */
  417. formatDate: function(dateString) {
  418. if (!dateString || dateString === '0000-00-00 00:00:00') {
  419. return '-';
  420. }
  421. var date = new Date(dateString);
  422. // Check if date is valid
  423. if (isNaN(date.getTime())) {
  424. return dateString;
  425. }
  426. return date.toLocaleDateString() + ' ' + date.toLocaleTimeString();
  427. },
  428. /**
  429. * Format file size
  430. */
  431. formatFileSize: function(bytes) {
  432. if (bytes === 0) return '0 Bytes';
  433. var k = 1024;
  434. var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
  435. var i = Math.floor(Math.log(bytes) / Math.log(k));
  436. return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
  437. },
  438. /**
  439. * Escape HTML
  440. */
  441. escapeHtml: function(text) {
  442. if (!text) return '';
  443. var map = {
  444. '&': '&amp;',
  445. '<': '&lt;',
  446. '>': '&gt;',
  447. '"': '&quot;',
  448. "'": '&#039;'
  449. };
  450. return text.toString().replace(/[&<>"']/g, function(m) { return map[m]; });
  451. },
  452. /**
  453. * Copy to clipboard
  454. */
  455. copyToClipboard: function(text) {
  456. var $temp = $('<input>');
  457. $('body').append($temp);
  458. $temp.val(text).select();
  459. document.execCommand('copy');
  460. $temp.remove();
  461. }
  462. };
  463. // Settings page functionality
  464. var QDR_TSS_Settings = {
  465. /**
  466. * Initialize the settings page
  467. */
  468. init: function() {
  469. // Initialize event handlers
  470. this.initEventHandlers();
  471. },
  472. /**
  473. * Initialize event handlers
  474. */
  475. initEventHandlers: function() {
  476. var self = this;
  477. // Generate new API key
  478. $('#qdr-tss-generate-key').on('click', function(e) {
  479. e.preventDefault();
  480. self.generateApiKey();
  481. });
  482. // Save settings
  483. $('#qdr-tss-settings-form').on('submit', function(e) {
  484. e.preventDefault();
  485. self.saveSettings();
  486. });
  487. // Purge expired files
  488. $('#qdr-tss-purge-expired').on('click', function(e) {
  489. e.preventDefault();
  490. self.purgeExpiredFiles();
  491. });
  492. },
  493. /**
  494. * Generate API key
  495. */
  496. generateApiKey: function() {
  497. // Generate a random string for API key
  498. var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  499. var apiKey = '';
  500. for (var i = 0; i < 32; i++) {
  501. apiKey += chars.charAt(Math.floor(Math.random() * chars.length));
  502. }
  503. $('#qdr-tss-api-key').val(apiKey);
  504. },
  505. /**
  506. * Save settings
  507. */
  508. saveSettings: function() {
  509. var data = {
  510. action: 'qdr_tss_save_settings',
  511. nonce: qdr_tss.nonce,
  512. api_key: $('#qdr-tss-api-key').val(),
  513. media_category: $('#qdr-tss-media-category').val(),
  514. max_upload_size: $('#qdr-tss-max-upload-size').val()
  515. };
  516. $.post(ajaxurl, data, function(response) {
  517. if (response.success) {
  518. alert(response.data.message);
  519. } else {
  520. alert(response.data.message);
  521. }
  522. });
  523. },
  524. /**
  525. * Purge expired files
  526. */
  527. purgeExpiredFiles: function() {
  528. if (!confirm(qdr_tss.strings.confirm_purge)) {
  529. return;
  530. }
  531. var data = {
  532. action: 'qdr_tss_purge_expired',
  533. nonce: qdr_tss.nonce
  534. };
  535. $.post(ajaxurl, data, function(response) {
  536. if (response.success) {
  537. alert(response.data.message);
  538. // Reload page to update storage usage
  539. location.reload();
  540. } else {
  541. alert(response.data.message);
  542. }
  543. });
  544. }
  545. };
  546. // Document ready
  547. $(function() {
  548. // Initialize appropriate page functionality
  549. if ($('.qdr-tss-table').length) {
  550. QDR_TSS_Media.init();
  551. }
  552. if ($('#qdr-tss-settings-form').length) {
  553. QDR_TSS_Settings.init();
  554. }
  555. });
  556. })(jQuery);