/**
* Admin JavaScript for QDR Temporary Shared Storage plugin
*
* Handles all admin-side functionality including:
* - Media page (loading, pagination, sorting, filtering, editing, deleting)
* - Settings page (API key generation, settings saving, purging expired files)
* - New actions: Copy permalink and Force Activate
* - Date validation for active period
*
* @package QDR_Temporary_Shared_Storage
* @since 1.0.0
*/
(function($) {
'use strict';
// Media page functionality
var QDR_TSS_Media = {
currentPage: 1,
perPage: 10,
totalPages: 1,
currentOrderBy: 'upload_date',
currentOrder: 'DESC',
currentSearchFilename: '',
currentSearchReference: '',
/**
* Initialize the media page
*/
init: function() {
// Load items on page load
this.loadItems();
// Initialize event handlers
this.initEventHandlers();
// Initialize datepicker
this.initDatepicker();
},
/**
* Initialize event handlers
*/
initEventHandlers: function() {
var self = this;
// Handle sorting
$('.qdr-tss-table th.sortable').on('click', function() {
var column = $(this).data('sort');
if (self.currentOrderBy === column) {
self.currentOrder = self.currentOrder === 'ASC' ? 'DESC' : 'ASC';
} else {
self.currentOrderBy = column;
self.currentOrder = 'ASC';
}
self.loadItems();
});
// Handle search
$('#qdr-tss-search-button').on('click', function() {
self.currentSearchFilename = $('#qdr-tss-search-filename').val();
self.currentSearchReference = $('#qdr-tss-search-reference').val();
self.currentPage = 1;
self.loadItems();
});
// Handle search reset
$('#qdr-tss-reset-search').on('click', function() {
$('#qdr-tss-search-filename').val('');
$('#qdr-tss-search-reference').val('');
self.currentSearchFilename = '';
self.currentSearchReference = '';
self.currentPage = 1;
self.loadItems();
});
// Handle refresh button
$('#qdr-tss-refresh').on('click', function() {
self.loadItems();
});
// Handle modal close
$('.qdr-tss-modal-close, .qdr-tss-modal-cancel').on('click', function() {
$('.qdr-tss-modal').hide();
});
// Close modal when clicking outside
$(window).on('click', function(e) {
if ($(e.target).hasClass('qdr-tss-modal')) {
$('.qdr-tss-modal').hide();
}
});
// Handle copy shortcode
$(document).on('click', '.qdr-tss-copy-shortcode', function() {
var shortcode = $('#qdr-tss-details-shortcode code').text();
self.copyToClipboard(shortcode);
alert(qdr_tss.strings.shortcode_copied);
});
// Handle copy permalink
$(document).on('click', '.qdr-tss-copy-permalink', function() {
var permalink = $('#qdr-tss-details-permalink a').attr('href');
self.copyToClipboard(permalink);
alert(qdr_tss.strings.permalink_copied);
});
// Handle edit item form submission
$('#qdr-tss-edit-form').on('submit', function(e) {
e.preventDefault();
// Validate dates before submitting
if (self.validateActivePeriod()) {
self.updateItem();
}
});
// Real-time date validation
$('#qdr-tss-edit-active-from, #qdr-tss-edit-active-to').on('change blur', function() {
self.validateActivePeriod();
});
// Handle confirm delete
$(document).on('click', '.qdr-tss-confirm-delete-button', function() {
var id = $('#qdr-tss-delete-id').val();
self.deleteItem(id);
});
},
/**
* Validate active period dates
*/
validateActivePeriod: function() {
var activeFrom = $('#qdr-tss-edit-active-from').val();
var activeTo = $('#qdr-tss-edit-active-to').val();
var errorContainer = $('#qdr-tss-date-validation-error');
// Remove existing error container
errorContainer.remove();
// If either field is empty, no validation needed yet
if (!activeFrom || !activeTo) {
return true;
}
var fromDate = new Date(activeFrom);
var toDate = new Date(activeTo);
// Check if dates are valid
if (isNaN(fromDate.getTime()) || isNaN(toDate.getTime())) {
this.showDateValidationError(qdr_tss.strings.invalid_date_format || 'Invalid date format');
return false;
}
// Check if active from is greater than or equal to active to
if (fromDate >= toDate) {
this.showDateValidationError(qdr_tss.strings.active_from_must_be_before_active_to || 'Active From date must be before Active To date');
return false;
}
return true;
},
/**
* Show date validation error
*/
showDateValidationError: function(message) {
var errorHtml = '
';
$('#qdr-tss-edit-active-to').closest('.qdr-tss-form-row').after(errorHtml);
},
/**
* Initialize datepicker
*/
initDatepicker: function() {
$(document).on('focus', '.qdr-tss-datepicker', function() {
var self = QDR_TSS_Media;
$(this).datepicker({
dateFormat: 'yy-mm-dd',
changeMonth: true,
changeYear: true,
onSelect: function() {
// Trigger validation when date is selected
setTimeout(function() {
self.validateActivePeriod();
}, 100);
}
});
});
},
/**
* Initialize action buttons
*/
initActionButtons: function() {
var self = this;
// View button
$('.qdr-tss-view-button').off('click').on('click', function() {
var id = $(this).data('id');
self.viewItem(id);
});
// Edit button
$('.qdr-tss-edit-button').off('click').on('click', function() {
var id = $(this).data('id');
self.editItem(id);
});
// Delete button
$('.qdr-tss-delete-button').off('click').on('click', function() {
var id = $(this).data('id');
self.showDeleteConfirmation(id);
});
// Copy permalink button (NEW)
$('.qdr-tss-copy-permalink-button').off('click').on('click', function() {
var id = $(this).data('id');
self.copyPermalinkToClipboard(id);
});
// Force activate button (NEW)
$('.qdr-tss-force-activate-button').off('click').on('click', function() {
var id = $(this).data('id');
self.forceActivateItem(id);
});
// Pagination
$('.qdr-tss-page-button').off('click').on('click', function() {
self.currentPage = parseInt($(this).data('page'));
self.loadItems();
});
},
/**
* Load items
*/
loadItems: function() {
var self = this;
var data = {
action: 'qdr_tss_get_items',
nonce: qdr_tss.nonce,
page: this.currentPage,
per_page: this.perPage,
orderby: this.currentOrderBy,
order: this.currentOrder,
original_file_name: this.currentSearchFilename,
reference: this.currentSearchReference
};
// Show loading indicator
$('#qdr-tss-items-list').html('| ' + qdr_tss.strings.loading + ' |
');
$.post(ajaxurl, data, function(response) {
if (response.success) {
self.renderItems(response.data.items);
self.renderPagination(response.data.total, response.data.total_pages);
} else {
alert(response.data.message || qdr_tss.strings.error_loading_items);
}
}).fail(function() {
$('#qdr-tss-items-list').html('| ' + qdr_tss.strings.error_loading_items_try_again + ' |
');
});
},
/**
* Render items
*/
renderItems: function(items) {
var self = this;
var $tbody = $('#qdr-tss-items-list');
$tbody.empty();
if (!items || items.length === 0) {
$tbody.append('| ' + qdr_tss.strings.no_items_found + ' |
');
return;
}
$.each(items, function(index, item) {
var statusClass = self.getItemStatusClass(item);
var row = '';
row += '| ' + item.fileid + ' | ';
row += '' + self.escapeHtml(item.original_file_name) + ' | ';
row += '' + self.escapeHtml(item.description || '') + ' | ';
row += '' + self.escapeHtml(item.message || '') + ' | ';
row += '' + self.formatDate(item.active_from) + ' | ';
row += '' + self.formatDate(item.active_to) + ' | ';
row += '' + self.escapeHtml(item.reference || '') + ' | ';
row += '' + self.formatDate(item.upload_date) + ' | ';
row += '' + item.download_count + ' | ';
row += '' + (item.last_download ? self.formatDate(item.last_download) : '-') + ' | ';
row += '';
row += ' ';
row += '';
row += '';
row += '';
row += '';
row += '';
row += ' ';
row += ' | ';
row += '
';
$tbody.append(row);
});
// Initialize action buttons
this.initActionButtons();
// Update sorting indicators
this.updateSortIndicators();
},
/**
* Copy permalink to clipboard (NEW FUNCTION)
*/
copyPermalinkToClipboard: function(id) {
var self = this;
var data = {
action: 'qdr_tss_get_item_details',
nonce: qdr_tss.nonce,
id: id
};
$.post(ajaxurl, data, function(response) {
if (response.success) {
var permalink = response.data.item.permalink;
self.copyToClipboard(permalink);
alert(qdr_tss.strings.permalink_copied_to_clipboard);
} else {
alert(response.data.message || qdr_tss.strings.error_getting_permalink);
}
}).fail(function() {
alert(qdr_tss.strings.error_getting_permalink_try_again);
});
},
/**
* Force activate item (NEW FUNCTION)
*/
forceActivateItem: function(id) {
var self = this;
if (!confirm(qdr_tss.strings.confirm_force_activate)) {
return;
}
var data = {
action: 'qdr_tss_force_activate_item',
nonce: qdr_tss.nonce,
id: id
};
$.post(ajaxurl, data, function(response) {
if (response.success) {
alert(response.data.message || qdr_tss.strings.item_force_activated);
// Reload items to show updated dates
self.loadItems();
} else {
alert(response.data.message || qdr_tss.strings.error_force_activating);
}
}).fail(function() {
alert(qdr_tss.strings.error_force_activating_try_again);
});
},
/**
* Get item status class
*/
getItemStatusClass: function(item) {
var now = new Date();
var activeFrom = new Date(item.active_from);
var activeTo = new Date(item.active_to);
if (now < activeFrom) {
return 'qdr-tss-status-pending';
} else if (now > activeTo) {
return 'qdr-tss-status-expired';
} else {
return 'qdr-tss-status-active';
}
},
/**
* Render pagination
*/
renderPagination: function(total, totalPages) {
var $info = $('.qdr-tss-pagination-info');
var $controls = $('.qdr-tss-pagination-controls');
// Update info
var startItem = (this.currentPage - 1) * this.perPage + 1;
var endItem = Math.min(this.currentPage * this.perPage, total);
if (total === 0) {
$info.html(qdr_tss.strings.no_items);
$controls.empty();
return;
}
$info.html(qdr_tss.strings.showing + ' ' + startItem + ' - ' + endItem + ' ' + qdr_tss.strings.of + ' ' + total);
// Update controls
$controls.empty();
if (totalPages <= 1) {
return;
}
// First page
if (this.currentPage > 1) {
$controls.append('');
}
// Previous page
if (this.currentPage > 1) {
$controls.append('');
}
// Page numbers
var startPage = Math.max(1, this.currentPage - 2);
var endPage = Math.min(totalPages, startPage + 4);
if (endPage - startPage < 4) {
startPage = Math.max(1, endPage - 4);
}
for (var i = startPage; i <= endPage; i++) {
var activeClass = i === this.currentPage ? 'button-primary' : '';
$controls.append('');
}
// Next page
if (this.currentPage < totalPages) {
$controls.append('');
}
// Last page
if (this.currentPage < totalPages) {
$controls.append('');
}
// Rebind click events
this.initActionButtons();
},
/**
* Update sort indicators
*/
updateSortIndicators: function() {
// Remove all indicators
$('.qdr-tss-table th').removeClass('sorted asc desc');
// Add indicator to current sort column
var $th = $('.qdr-tss-table th[data-sort="' + this.currentOrderBy + '"]');
$th.addClass('sorted ' + this.currentOrder.toLowerCase());
},
/**
* View item details
*/
viewItem: function(id) {
var self = this;
// Show loading modal
$('#qdr-tss-details-modal').show();
$('.qdr-tss-details-content').html(' ' + qdr_tss.strings.loading + '
');
// Get additional data via AJAX
var data = {
action: 'qdr_tss_get_item_details',
nonce: qdr_tss.nonce,
id: id
};
$.post(ajaxurl, data, function(response) {
if (response.success) {
var item = response.data.item;
// Populate details
$('#qdr-tss-details-fileid').text(item.fileid);
$('#qdr-tss-details-filename').text(item.original_file_name);
$('#qdr-tss-details-description').text(item.description || '-');
$('#qdr-tss-details-message').text(item.message || '-');
$('#qdr-tss-details-reference').text(item.reference || '-');
$('#qdr-tss-details-active-period').text(self.formatDate(item.active_from) + ' - ' + self.formatDate(item.active_to));
$('#qdr-tss-details-filesize').text(self.formatFileSize(item.file_size));
$('#qdr-tss-details-download-count').text(item.download_count);
$('#qdr-tss-details-last-download').text(item.last_download ? self.formatDate(item.last_download) : '-');
// Permalink and shortcode
$('#qdr-tss-details-permalink').html('' + item.permalink + '');
$('#qdr-tss-details-shortcode').html('' + item.shortcode + '');
// Initialize copy buttons
$('.qdr-tss-copy-shortcode').data('text', item.shortcode);
$('.qdr-tss-copy-permalink').data('text', item.permalink);
} else {
alert(response.data.message || qdr_tss.strings.error_loading_item_details);
$('#qdr-tss-details-modal').hide();
}
}).fail(function() {
alert(qdr_tss.strings.error_loading_item_details_try_again);
$('#qdr-tss-details-modal').hide();
});
},
/**
* Edit item
*/
editItem: function(id) {
// Find item in the table
var $row = $('tr[data-id="' + id + '"]');
if ($row.length === 0) return;
var $cells = $row.find('td');
// Populate form fields
$('#qdr-tss-edit-id').val(id);
$('#qdr-tss-edit-description').val($cells.eq(2).text());
$('#qdr-tss-edit-message').val($cells.eq(3).text());
// Parse dates for datepicker
var activeFrom = $cells.eq(4).text();
var activeTo = $cells.eq(5).text();
$('#qdr-tss-edit-active-from').val(activeFrom !== '-' ? activeFrom : '');
$('#qdr-tss-edit-active-to').val(activeTo !== '-' ? activeTo : '');
$('#qdr-tss-edit-reference').val($cells.eq(6).text());
// Clear status
$('#qdr-tss-edit-status').html('');
// Remove any existing validation errors
$('#qdr-tss-date-validation-error').remove();
// Show modal
$('#qdr-tss-edit-modal').show();
},
/**
* Update item
*/
updateItem: function() {
var self = this;
// Show loading status
$('#qdr-tss-edit-status').html(' ' + qdr_tss.strings.saving + '
');
var data = {
action: 'qdr_tss_edit_item',
nonce: qdr_tss.nonce,
id: $('#qdr-tss-edit-id').val(),
description: $('#qdr-tss-edit-description').val(),
message: $('#qdr-tss-edit-message').val(),
active_from: $('#qdr-tss-edit-active-from').val(),
active_to: $('#qdr-tss-edit-active-to').val(),
reference: $('#qdr-tss-edit-reference').val()
};
$.post(ajaxurl, data, function(response) {
if (response.success) {
// Show success status
$('#qdr-tss-edit-status').html('' + (response.data.message || qdr_tss.strings.changes_saved) + '
');
// Hide modal after a delay
setTimeout(function() {
$('#qdr-tss-edit-modal').hide();
// Reload items
self.loadItems();
}, 1000);
} else {
// Show error status
$('#qdr-tss-edit-status').html('' + (response.data.message || qdr_tss.strings.error_saving_changes) + '
');
}
}).fail(function() {
// Show error status
$('#qdr-tss-edit-status').html('' + qdr_tss.strings.error_saving_changes_try_again + '
');
});
},
/**
* Show delete confirmation
*/
showDeleteConfirmation: function(id) {
$('#qdr-tss-delete-id').val(id);
$('#qdr-tss-confirm-delete-modal').show();
},
/**
* Delete item
*/
deleteItem: function(id) {
var self = this;
var data = {
action: 'qdr_tss_delete_item',
nonce: qdr_tss.nonce,
id: id
};
$.post(ajaxurl, data, function(response) {
if (response.success) {
// Hide modal
$('#qdr-tss-confirm-delete-modal').hide();
// Show success message
alert(response.data.message || qdr_tss.strings.file_deleted);
// Reload items
self.loadItems();
} else {
alert(response.data.message || qdr_tss.strings.error_deleting_file);
}
}).fail(function() {
alert(qdr_tss.strings.error_deleting_file_try_again);
});
},
/**
* Format date
*/
formatDate: function(dateString) {
if (!dateString || dateString === '0000-00-00 00:00:00') {
return '-';
}
var date = new Date(dateString);
// Check if date is valid
if (isNaN(date.getTime())) {
return dateString;
}
return date.toLocaleDateString() + ' ' + date.toLocaleTimeString();
},
/**
* Format file size
*/
formatFileSize: function(bytes) {
if (bytes === 0) return '0 Bytes';
var k = 1024;
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
var i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
},
/**
* Escape HTML
*/
escapeHtml: function(text) {
if (!text) return '';
var map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
return text.toString().replace(/[&<>"']/g, function(m) { return map[m]; });
},
/**
* Copy to clipboard
*/
copyToClipboard: function(text) {
var $temp = $('');
$('body').append($temp);
$temp.val(text).select();
document.execCommand('copy');
$temp.remove();
}
};
// Settings page functionality
var QDR_TSS_Settings = {
/**
* Initialize the settings page
*/
init: function() {
// Initialize event handlers
this.initEventHandlers();
},
/**
* Initialize event handlers
*/
initEventHandlers: function() {
var self = this;
// Generate new API key
$('#qdr-tss-generate-key').on('click', function(e) {
e.preventDefault();
self.generateApiKey();
});
// Save settings
$('#qdr-tss-settings-form').on('submit', function(e) {
e.preventDefault();
self.saveSettings();
});
// Purge expired files
$('#qdr-tss-purge-expired').on('click', function(e) {
e.preventDefault();
self.purgeExpiredFiles();
});
},
/**
* Generate API key
*/
generateApiKey: function() {
// Generate a random string for API key
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var apiKey = '';
for (var i = 0; i < 32; i++) {
apiKey += chars.charAt(Math.floor(Math.random() * chars.length));
}
$('#qdr-tss-api-key').val(apiKey);
},
/**
* Save settings
*/
saveSettings: function() {
var data = {
action: 'qdr_tss_save_settings',
nonce: qdr_tss.nonce,
api_key: $('#qdr-tss-api-key').val(),
media_category: $('#qdr-tss-media-category').val(),
max_upload_size: $('#qdr-tss-max-upload-size').val()
};
$.post(ajaxurl, data, function(response) {
if (response.success) {
alert(response.data.message);
} else {
alert(response.data.message);
}
});
},
/**
* Purge expired files
*/
purgeExpiredFiles: function() {
if (!confirm(qdr_tss.strings.confirm_purge)) {
return;
}
var data = {
action: 'qdr_tss_purge_expired',
nonce: qdr_tss.nonce
};
$.post(ajaxurl, data, function(response) {
if (response.success) {
alert(response.data.message);
// Reload page to update storage usage
location.reload();
} else {
alert(response.data.message);
}
});
}
};
// Document ready
$(function() {
// Initialize appropriate page functionality
if ($('.qdr-tss-table').length) {
QDR_TSS_Media.init();
}
if ($('#qdr-tss-settings-form').length) {
QDR_TSS_Settings.init();
}
});
})(jQuery);