|
|
@@ -0,0 +1,1235 @@
|
|
|
+<?php
|
|
|
+/**
|
|
|
+ * Plugin Name: QDR - Temporary Shared Storage (via REST API)
|
|
|
+ * Plugin URI: https://www.quadarax.com/plugins/qdr-temporary-shared-storage
|
|
|
+ * Description: Extends WordPress functionality for REST API interface to upload temporary media files and provide permalink to download to anybody who has permalink (via password protected download page)
|
|
|
+ * Version: 1.0.0
|
|
|
+ * Requires at least: 6.8.1
|
|
|
+ * Requires PHP: 8.2
|
|
|
+ * Author: Dalibor Votruba
|
|
|
+ * Author URI: https://www.quadarax.com
|
|
|
+ * License: GPL v2 or later
|
|
|
+ * License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
|
|
+ * Text Domain: qdr-temporary-shared-storage
|
|
|
+ * Domain Path: /languages
|
|
|
+ * WC requires at least: 9.0
|
|
|
+ * WC tested up to: 9.8
|
|
|
+ */
|
|
|
+
|
|
|
+// If this file is called directly, abort.
|
|
|
+if (!defined('WPINC')) {
|
|
|
+ die;
|
|
|
+}
|
|
|
+
|
|
|
+// Define plugin constants
|
|
|
+define('QDR_TSS_VERSION', '1.0.0');
|
|
|
+define('QDR_TSS_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
|
|
+define('QDR_TSS_PLUGIN_URL', plugin_dir_url(__FILE__));
|
|
|
+define('QDR_TSS_PLUGIN_BASENAME', plugin_basename(__FILE__));
|
|
|
+define('QDR_TSS_UPLOADS_DIR', wp_upload_dir()['basedir'] . '/qdr-shared-storage/');
|
|
|
+define('QDR_TSS_UPLOADS_URL', wp_upload_dir()['baseurl'] . '/qdr-shared-storage/');
|
|
|
+define('QDR_TSS_PLUGIN_TABLE', 'qdr_temporary_shared_storage');
|
|
|
+
|
|
|
+/**
|
|
|
+ * The core plugin class
|
|
|
+ */
|
|
|
+class QDR_Temporary_Shared_Storage {
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Plugin instance.
|
|
|
+ *
|
|
|
+ * @var QDR_Temporary_Shared_Storage
|
|
|
+ */
|
|
|
+ private static $instance = null;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Plugin settings.
|
|
|
+ *
|
|
|
+ * @var array
|
|
|
+ */
|
|
|
+ private $settings;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get plugin instance.
|
|
|
+ *
|
|
|
+ * @return QDR_Temporary_Shared_Storage
|
|
|
+ */
|
|
|
+ public static function get_instance() {
|
|
|
+ if (null === self::$instance) {
|
|
|
+ self::$instance = new self();
|
|
|
+ }
|
|
|
+ return self::$instance;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Constructor.
|
|
|
+ */
|
|
|
+ private function __construct() {
|
|
|
+ // Load plugin textdomain for translations
|
|
|
+ add_action('plugins_loaded', array($this, 'load_textdomain'));
|
|
|
+
|
|
|
+ // Register activation and deactivation hooks
|
|
|
+ register_activation_hook(__FILE__, array($this, 'activate'));
|
|
|
+ register_deactivation_hook(__FILE__, array($this, 'deactivate'));
|
|
|
+
|
|
|
+ // Initialize the plugin
|
|
|
+ add_action('init', array($this, 'init'));
|
|
|
+
|
|
|
+ // Add admin menu
|
|
|
+ add_action('admin_menu', array($this, 'add_admin_menu'));
|
|
|
+
|
|
|
+ // Register REST API routes
|
|
|
+ add_action('rest_api_init', array($this, 'register_rest_routes'));
|
|
|
+
|
|
|
+ // Register shortcode
|
|
|
+ add_shortcode('qdr_tss_download', array($this, 'download_shortcode'));
|
|
|
+
|
|
|
+ // Schedule cron job
|
|
|
+ if (!wp_next_scheduled('qdr_tss_purge_expired_files')) {
|
|
|
+ wp_schedule_event(time(), 'hourly', 'qdr_tss_purge_expired_files');
|
|
|
+ }
|
|
|
+ add_action('qdr_tss_purge_expired_files', array($this, 'purge_expired_files'));
|
|
|
+
|
|
|
+ // Load settings
|
|
|
+ $this->settings = get_option('qdr_tss_settings', array(
|
|
|
+ 'api_key' => wp_generate_password(32, false),
|
|
|
+ 'media_category' => 'shared-storage',
|
|
|
+ 'max_upload_size' => 1073741824, // 1GB default
|
|
|
+ ));
|
|
|
+
|
|
|
+ // Add REST API check
|
|
|
+ add_filter('rest_authentication_errors', array($this, 'check_api_key'));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Load plugin textdomain.
|
|
|
+ */
|
|
|
+ public function load_textdomain() {
|
|
|
+ load_plugin_textdomain('qdr-temporary-shared-storage', false, dirname(plugin_basename(__FILE__)) . '/languages');
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Plugin activation.
|
|
|
+ */
|
|
|
+ public function activate() {
|
|
|
+ global $wpdb;
|
|
|
+
|
|
|
+ // Create uploads directory if it doesn't exist
|
|
|
+ if (!file_exists(QDR_TSS_UPLOADS_DIR)) {
|
|
|
+ wp_mkdir_p(QDR_TSS_UPLOADS_DIR);
|
|
|
+
|
|
|
+ // Create .htaccess file to protect direct access
|
|
|
+ $htaccess_content = "# Disable directory browsing\nOptions -Indexes\n\n# Deny direct access to all files\n<FilesMatch \".*\">\nOrder Allow,Deny\nDeny from all\n</FilesMatch>";
|
|
|
+ file_put_contents(QDR_TSS_UPLOADS_DIR . '.htaccess', $htaccess_content);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Create custom table
|
|
|
+ $table_name = $wpdb->prefix . QDR_TSS_PLUGIN_TABLE;
|
|
|
+ $charset_collate = $wpdb->get_charset_collate();
|
|
|
+
|
|
|
+ $sql = "CREATE TABLE $table_name (
|
|
|
+ id bigint(20) NOT NULL AUTO_INCREMENT,
|
|
|
+ media_id bigint(20) NOT NULL,
|
|
|
+ original_file_name varchar(255) NOT NULL,
|
|
|
+ description text NULL,
|
|
|
+ message text NULL,
|
|
|
+ active_from datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
|
|
|
+ active_to datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
|
|
|
+ password varchar(255) NOT NULL,
|
|
|
+ reference varchar(255) NULL,
|
|
|
+ upload_date datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
|
|
+ download_count int DEFAULT 0 NOT NULL,
|
|
|
+ last_download datetime DEFAULT '0000-00-00 00:00:00' NULL,
|
|
|
+ file_path varchar(255) NOT NULL,
|
|
|
+ file_size bigint(20) NOT NULL,
|
|
|
+ PRIMARY KEY (id),
|
|
|
+ KEY media_id (media_id),
|
|
|
+ KEY reference (reference),
|
|
|
+ KEY active_to (active_to)
|
|
|
+ ) $charset_collate;";
|
|
|
+
|
|
|
+ require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
|
|
+ dbDelta($sql);
|
|
|
+
|
|
|
+ // Default settings
|
|
|
+ if (!get_option('qdr_tss_settings')) {
|
|
|
+ add_option('qdr_tss_settings', array(
|
|
|
+ 'api_key' => wp_generate_password(32, false),
|
|
|
+ 'media_category' => 'shared-storage',
|
|
|
+ 'max_upload_size' => 1073741824, // 1GB default
|
|
|
+ ));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Plugin deactivation.
|
|
|
+ */
|
|
|
+ public function deactivate() {
|
|
|
+ // Clear scheduled cron job
|
|
|
+ wp_clear_scheduled_hook('qdr_tss_purge_expired_files');
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Initialize plugin.
|
|
|
+ */
|
|
|
+ public function init() {
|
|
|
+ // Register scripts and styles
|
|
|
+ add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
|
|
|
+
|
|
|
+ // Create download page if it doesn't exist
|
|
|
+ $download_page = get_page_by_path('shared-file-download');
|
|
|
+ if (!$download_page) {
|
|
|
+ $page_id = wp_insert_post(array(
|
|
|
+ 'post_title' => __('Shared File Download', 'qdr-temporary-shared-storage'),
|
|
|
+ 'post_content' => '[qdr_tss_download]',
|
|
|
+ 'post_status' => 'publish',
|
|
|
+ 'post_type' => 'page',
|
|
|
+ 'post_name' => 'shared-file-download'
|
|
|
+ ));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Enqueue admin scripts and styles.
|
|
|
+ */
|
|
|
+ public function enqueue_admin_scripts($hook) {
|
|
|
+ // Only load on plugin admin pages
|
|
|
+ if (strpos($hook, 'qdr-temporary-shared-storage') === false) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ wp_enqueue_style('qdr-tss-admin-css', QDR_TSS_PLUGIN_URL . 'assets/css/admin.css', array(), QDR_TSS_VERSION);
|
|
|
+ wp_enqueue_script('qdr-tss-admin-js', QDR_TSS_PLUGIN_URL . 'assets/js/admin.js', array('jquery', 'jquery-ui-datepicker'), QDR_TSS_VERSION, true);
|
|
|
+
|
|
|
+ // Add datepicker
|
|
|
+ wp_enqueue_style('jquery-ui-datepicker');
|
|
|
+
|
|
|
+ // Localize script
|
|
|
+ wp_localize_script('qdr-tss-admin-js', 'qdr_tss', array(
|
|
|
+ 'ajaxurl' => admin_url('admin-ajax.php'),
|
|
|
+ 'nonce' => wp_create_nonce('qdr_tss_nonce'),
|
|
|
+ 'strings' => array(
|
|
|
+ 'confirm_delete' => __('Are you sure you want to delete this file?', 'qdr-temporary-shared-storage'),
|
|
|
+ 'confirm_purge' => __('Are you sure you want to purge all expired files?', 'qdr-temporary-shared-storage')
|
|
|
+ )
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Add admin menu items.
|
|
|
+ */
|
|
|
+ public function add_admin_menu() {
|
|
|
+ // Add Shared Storage page under Media menu
|
|
|
+ add_submenu_page(
|
|
|
+ 'upload.php',
|
|
|
+ __('Shared Storage', 'qdr-temporary-shared-storage'),
|
|
|
+ __('Shared Storage', 'qdr-temporary-shared-storage'),
|
|
|
+ 'manage_options',
|
|
|
+ 'qdr-temporary-shared-storage',
|
|
|
+ array($this, 'render_media_page')
|
|
|
+ );
|
|
|
+
|
|
|
+ // Add Settings page
|
|
|
+ add_submenu_page(
|
|
|
+ 'options-general.php',
|
|
|
+ __('Shared Storage Settings', 'qdr-temporary-shared-storage'),
|
|
|
+ __('Shared Storage', 'qdr-temporary-shared-storage'),
|
|
|
+ 'manage_options',
|
|
|
+ 'qdr-temporary-shared-storage-settings',
|
|
|
+ array($this, 'render_settings_page')
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Render the Media Shared Storage admin page.
|
|
|
+ */
|
|
|
+ public function render_media_page() {
|
|
|
+ include_once QDR_TSS_PLUGIN_DIR . 'admin/media-page.php';
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Render the Settings admin page.
|
|
|
+ */
|
|
|
+ public function render_settings_page() {
|
|
|
+ include_once QDR_TSS_PLUGIN_DIR . 'admin/settings-page.php';
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Register REST API routes.
|
|
|
+ */
|
|
|
+ public function register_rest_routes() {
|
|
|
+ register_rest_route('qdr-tss/v1', '/media', array(
|
|
|
+ 'methods' => 'POST',
|
|
|
+ 'callback' => array($this, 'upload_media'),
|
|
|
+ 'permission_callback' => array($this, 'check_rest_permission')
|
|
|
+ ));
|
|
|
+
|
|
|
+ register_rest_route('qdr-tss/v1', '/media/(?P<id>\d+)', array(
|
|
|
+ 'methods' => 'GET',
|
|
|
+ 'callback' => array($this, 'get_media'),
|
|
|
+ 'permission_callback' => array($this, 'check_rest_permission')
|
|
|
+ ));
|
|
|
+
|
|
|
+ register_rest_route('qdr-tss/v1', '/media/(?P<id>\d+)', array(
|
|
|
+ 'methods' => 'PUT',
|
|
|
+ 'callback' => array($this, 'update_media'),
|
|
|
+ 'permission_callback' => array($this, 'check_rest_permission')
|
|
|
+ ));
|
|
|
+
|
|
|
+ register_rest_route('qdr-tss/v1', '/media/(?P<id>\d+)', array(
|
|
|
+ 'methods' => 'DELETE',
|
|
|
+ 'callback' => array($this, 'delete_media'),
|
|
|
+ 'permission_callback' => array($this, 'check_rest_permission')
|
|
|
+ ));
|
|
|
+
|
|
|
+ register_rest_route('qdr-tss/v1', '/media', array(
|
|
|
+ 'methods' => 'GET',
|
|
|
+ 'callback' => array($this, 'get_media_collection'),
|
|
|
+ 'permission_callback' => array($this, 'check_rest_permission')
|
|
|
+ ));
|
|
|
+
|
|
|
+ // Chunked upload endpoints
|
|
|
+ register_rest_route('qdr-tss/v1', '/upload/init', array(
|
|
|
+ 'methods' => 'POST',
|
|
|
+ 'callback' => array($this, 'init_chunked_upload'),
|
|
|
+ 'permission_callback' => array($this, 'check_rest_permission')
|
|
|
+ ));
|
|
|
+
|
|
|
+ register_rest_route('qdr-tss/v1', '/upload/chunk', array(
|
|
|
+ 'methods' => 'POST',
|
|
|
+ 'callback' => array($this, 'upload_chunk'),
|
|
|
+ 'permission_callback' => array($this, 'check_rest_permission')
|
|
|
+ ));
|
|
|
+
|
|
|
+ register_rest_route('qdr-tss/v1', '/upload/finalize', array(
|
|
|
+ 'methods' => 'POST',
|
|
|
+ 'callback' => array($this, 'finalize_chunked_upload'),
|
|
|
+ 'permission_callback' => array($this, 'check_rest_permission')
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Check API key for REST API requests.
|
|
|
+ */
|
|
|
+ public function check_api_key($result) {
|
|
|
+ // Only check for our namespace
|
|
|
+ if (empty($result) && strpos($_SERVER['REQUEST_URI'], '/wp-json/qdr-tss/') !== false) {
|
|
|
+ $headers = getallheaders();
|
|
|
+ $api_key = isset($headers['X-Api-Key']) ? $headers['X-Api-Key'] : '';
|
|
|
+
|
|
|
+ if ($api_key !== $this->settings['api_key']) {
|
|
|
+ return new WP_Error(
|
|
|
+ 'rest_forbidden',
|
|
|
+ __('Invalid API key.', 'qdr-temporary-shared-storage'),
|
|
|
+ array('status' => 403)
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return $result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Check REST API permissions.
|
|
|
+ */
|
|
|
+ public function check_rest_permission() {
|
|
|
+ $headers = getallheaders();
|
|
|
+ $api_key = isset($headers['X-Api-Key']) ? $headers['X-Api-Key'] : '';
|
|
|
+
|
|
|
+ return ($api_key === $this->settings['api_key']);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Initialize chunked upload.
|
|
|
+ */
|
|
|
+ public function init_chunked_upload($request) {
|
|
|
+ $params = $request->get_params();
|
|
|
+
|
|
|
+ // Check required parameters
|
|
|
+ if (!isset($params['original_file_name']) || !isset($params['file_size'])) {
|
|
|
+ return new WP_Error('missing_params', __('Missing required parameters.', 'qdr-temporary-shared-storage'), array('status' => 400));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check max upload size
|
|
|
+ if (intval($params['file_size']) > $this->settings['max_upload_size']) {
|
|
|
+ return new WP_Error('file_too_large', __('File exceeds maximum upload size.', 'qdr-temporary-shared-storage'), array('status' => 400));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check total storage usage
|
|
|
+ if (!$this->check_storage_limit(intval($params['file_size']))) {
|
|
|
+ return new WP_Error('storage_limit', __('Storage limit exceeded.', 'qdr-temporary-shared-storage'), array('status' => 400));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Create temporary upload directory
|
|
|
+ $upload_id = uniqid();
|
|
|
+ $upload_dir = QDR_TSS_UPLOADS_DIR . 'tmp/' . $upload_id;
|
|
|
+
|
|
|
+ if (!file_exists(QDR_TSS_UPLOADS_DIR . 'tmp/')) {
|
|
|
+ wp_mkdir_p(QDR_TSS_UPLOADS_DIR . 'tmp/');
|
|
|
+ }
|
|
|
+
|
|
|
+ wp_mkdir_p($upload_dir);
|
|
|
+
|
|
|
+ return array(
|
|
|
+ 'upload_id' => $upload_id,
|
|
|
+ 'status' => 'initialized'
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Upload a chunk.
|
|
|
+ */
|
|
|
+ public function upload_chunk($request) {
|
|
|
+ $upload_id = $request->get_param('upload_id');
|
|
|
+ $chunk_index = $request->get_param('chunk_index');
|
|
|
+ $total_chunks = $request->get_param('total_chunks');
|
|
|
+
|
|
|
+ if (!$upload_id || !isset($chunk_index) || !isset($total_chunks)) {
|
|
|
+ return new WP_Error('missing_params', __('Missing required parameters.', 'qdr-temporary-shared-storage'), array('status' => 400));
|
|
|
+ }
|
|
|
+
|
|
|
+ $upload_dir = QDR_TSS_UPLOADS_DIR . 'tmp/' . $upload_id;
|
|
|
+
|
|
|
+ // Check if the upload directory exists
|
|
|
+ if (!file_exists($upload_dir)) {
|
|
|
+ return new WP_Error('invalid_upload_id', __('Invalid upload ID.', 'qdr-temporary-shared-storage'), array('status' => 400));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Handle file upload
|
|
|
+ $files = $request->get_file_params();
|
|
|
+
|
|
|
+ if (empty($files) || !isset($files['chunk'])) {
|
|
|
+ return new WP_Error('no_file', __('No file uploaded.', 'qdr-temporary-shared-storage'), array('status' => 400));
|
|
|
+ }
|
|
|
+
|
|
|
+ $chunk_file = $files['chunk'];
|
|
|
+
|
|
|
+ // Move uploaded chunk to temporary directory
|
|
|
+ $chunk_path = $upload_dir . '/' . $chunk_index;
|
|
|
+ move_uploaded_file($chunk_file['tmp_name'], $chunk_path);
|
|
|
+
|
|
|
+ return array(
|
|
|
+ 'upload_id' => $upload_id,
|
|
|
+ 'chunk_index' => $chunk_index,
|
|
|
+ 'status' => 'chunk_uploaded'
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Finalize chunked upload.
|
|
|
+ */
|
|
|
+ public function finalize_chunked_upload($request) {
|
|
|
+ global $wpdb;
|
|
|
+
|
|
|
+ $params = $request->get_params();
|
|
|
+ $upload_id = $params['upload_id'];
|
|
|
+ $total_chunks = $params['total_chunks'];
|
|
|
+
|
|
|
+ if (!$upload_id || !isset($total_chunks)) {
|
|
|
+ return new WP_Error('missing_params', __('Missing required parameters.', 'qdr-temporary-shared-storage'), array('status' => 400));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check for required file metadata
|
|
|
+ $required_fields = array('original_file_name', 'description', 'password');
|
|
|
+ foreach ($required_fields as $field) {
|
|
|
+ if (!isset($params[$field]) || empty($params[$field])) {
|
|
|
+ return new WP_Error('missing_field', sprintf(__('Missing required field: %s', 'qdr-temporary-shared-storage'), $field), array('status' => 400));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $upload_dir = QDR_TSS_UPLOADS_DIR . 'tmp/' . $upload_id;
|
|
|
+
|
|
|
+ // Check that all chunks are present
|
|
|
+ for ($i = 0; $i < $total_chunks; $i++) {
|
|
|
+ if (!file_exists($upload_dir . '/' . $i)) {
|
|
|
+ return new WP_Error('missing_chunks', __('Not all chunks have been uploaded.', 'qdr-temporary-shared-storage'), array('status' => 400));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Create final file directory
|
|
|
+ $file_dir = QDR_TSS_UPLOADS_DIR . date('Y/m/d');
|
|
|
+ if (!file_exists($file_dir)) {
|
|
|
+ wp_mkdir_p($file_dir);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Sanitize file name
|
|
|
+ $original_file_name = sanitize_file_name($params['original_file_name']);
|
|
|
+ $file_name = md5($original_file_name . time()) . '-' . $original_file_name;
|
|
|
+ $file_path = $file_dir . '/' . $file_name;
|
|
|
+
|
|
|
+ // Combine chunks
|
|
|
+ $final_file = fopen($file_path, 'wb');
|
|
|
+
|
|
|
+ for ($i = 0; $i < $total_chunks; $i++) {
|
|
|
+ $chunk_path = $upload_dir . '/' . $i;
|
|
|
+ $chunk = fopen($chunk_path, 'rb');
|
|
|
+ stream_copy_to_stream($chunk, $final_file);
|
|
|
+ fclose($chunk);
|
|
|
+ unlink($chunk_path);
|
|
|
+ }
|
|
|
+
|
|
|
+ fclose($final_file);
|
|
|
+
|
|
|
+ // Clean up temporary directory
|
|
|
+ rmdir($upload_dir);
|
|
|
+
|
|
|
+ // Get file size
|
|
|
+ $file_size = filesize($file_path);
|
|
|
+
|
|
|
+ // Set dates
|
|
|
+ $active_from = !empty($params['active_from']) ? $params['active_from'] : current_time('mysql');
|
|
|
+ $active_to = !empty($params['active_to']) ? $params['active_to'] : date('Y-m-d H:i:s', strtotime('+30 days'));
|
|
|
+
|
|
|
+ // Generate a unique media ID and insert into database
|
|
|
+ $media_id = time() . rand(1000, 9999);
|
|
|
+
|
|
|
+ $table_name = $wpdb->prefix . QDR_TSS_PLUGIN_TABLE;
|
|
|
+
|
|
|
+ $wpdb->insert(
|
|
|
+ $table_name,
|
|
|
+ array(
|
|
|
+ 'media_id' => $media_id,
|
|
|
+ 'original_file_name' => $original_file_name,
|
|
|
+ 'description' => sanitize_textarea_field($params['description']),
|
|
|
+ 'message' => isset($params['message']) ? sanitize_textarea_field($params['message']) : '',
|
|
|
+ 'active_from' => $active_from,
|
|
|
+ 'active_to' => $active_to,
|
|
|
+ 'password' => wp_hash_password($params['password']),
|
|
|
+ 'reference' => isset($params['reference']) ? sanitize_text_field($params['reference']) : '',
|
|
|
+ 'upload_date' => current_time('mysql'),
|
|
|
+ 'file_path' => str_replace(QDR_TSS_UPLOADS_DIR, '', $file_path),
|
|
|
+ 'file_size' => $file_size
|
|
|
+ ),
|
|
|
+ array('%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%d')
|
|
|
+ );
|
|
|
+
|
|
|
+ $download_url = site_url('shared-file-download/' . $media_id);
|
|
|
+ $shortcode = '[qdr_tss_download id="' . $media_id . '"]';
|
|
|
+
|
|
|
+ return array(
|
|
|
+ 'media_id' => $media_id,
|
|
|
+ 'permalink' => $download_url,
|
|
|
+ 'shortcode' => $shortcode
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get single media details.
|
|
|
+ */
|
|
|
+ public function get_media($request) {
|
|
|
+ global $wpdb;
|
|
|
+
|
|
|
+ $id = $request->get_param('id');
|
|
|
+ $table_name = $wpdb->prefix . QDR_TSS_PLUGIN_TABLE;
|
|
|
+
|
|
|
+ $item = $wpdb->get_row($wpdb->prepare(
|
|
|
+ "SELECT media_id, original_file_name, description, message, active_from, active_to, reference, upload_date, download_count, last_download, file_size
|
|
|
+ FROM $table_name
|
|
|
+ WHERE media_id = %d",
|
|
|
+ $id
|
|
|
+ ));
|
|
|
+
|
|
|
+ if (!$item) {
|
|
|
+ return new WP_Error('not_found', __('Media not found.', 'qdr-temporary-shared-storage'), array('status' => 404));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Get additional information from WordPress attachment
|
|
|
+ $attachment = get_post($id);
|
|
|
+ if ($attachment && $attachment->post_type === 'attachment') {
|
|
|
+ $item->attachment_url = wp_get_attachment_url($id);
|
|
|
+ $item->attachment_title = $attachment->post_title;
|
|
|
+ }
|
|
|
+
|
|
|
+ return $item;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Update media details.
|
|
|
+ */
|
|
|
+ public function update_media($request) {
|
|
|
+ global $wpdb;
|
|
|
+
|
|
|
+ $id = $request->get_param('id');
|
|
|
+ $params = $request->get_params();
|
|
|
+
|
|
|
+ $table_name = $wpdb->prefix . QDR_TSS_PLUGIN_TABLE;
|
|
|
+
|
|
|
+ // Check if media exists in our custom table
|
|
|
+ $item = $wpdb->get_row($wpdb->prepare(
|
|
|
+ "SELECT id FROM $table_name WHERE media_id = %d",
|
|
|
+ $id
|
|
|
+ ));
|
|
|
+
|
|
|
+ if (!$item) {
|
|
|
+ return new WP_Error('not_found', __('Media not found.', 'qdr-temporary-shared-storage'), array('status' => 404));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check if the WordPress attachment exists
|
|
|
+ $attachment = get_post($id);
|
|
|
+ if (!$attachment || $attachment->post_type !== 'attachment') {
|
|
|
+ return new WP_Error('attachment_not_found', __('WordPress attachment not found.', 'qdr-temporary-shared-storage'), array('status' => 404));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Prepare update data for our custom table
|
|
|
+ $update_data = array();
|
|
|
+ $update_format = array();
|
|
|
+
|
|
|
+ if (isset($params['message'])) {
|
|
|
+ $update_data['message'] = sanitize_textarea_field($params['message']);
|
|
|
+ $update_format[] = '%s';
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isset($params['active_from'])) {
|
|
|
+ $update_data['active_from'] = $params['active_from'];
|
|
|
+ $update_format[] = '%s';
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isset($params['active_to'])) {
|
|
|
+ $update_data['active_to'] = $params['active_to'];
|
|
|
+ $update_format[] = '%s';
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isset($params['reference'])) {
|
|
|
+ $update_data['reference'] = sanitize_text_field($params['reference']);
|
|
|
+ $update_format[] = '%s';
|
|
|
+ }
|
|
|
+
|
|
|
+ if (empty($update_data)) {
|
|
|
+ return new WP_Error('no_changes', __('No changes to update.', 'qdr-temporary-shared-storage'), array('status' => 400));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Update in our custom table
|
|
|
+ $wpdb->update(
|
|
|
+ $table_name,
|
|
|
+ $update_data,
|
|
|
+ array('media_id' => $id),
|
|
|
+ $update_format,
|
|
|
+ array('%d')
|
|
|
+ );
|
|
|
+
|
|
|
+ // Update WordPress attachment if description is provided
|
|
|
+ if (isset($params['description'])) {
|
|
|
+ $description = sanitize_textarea_field($params['description']);
|
|
|
+ $attachment_data = array(
|
|
|
+ 'ID' => $id,
|
|
|
+ 'post_excerpt' => $description
|
|
|
+ );
|
|
|
+ wp_update_post($attachment_data);
|
|
|
+
|
|
|
+ // Also update our custom table
|
|
|
+ $wpdb->update(
|
|
|
+ $table_name,
|
|
|
+ array('description' => $description),
|
|
|
+ array('media_id' => $id),
|
|
|
+ array('%s'),
|
|
|
+ array('%d')
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ // Get updated record
|
|
|
+ $updated_item = $wpdb->get_row($wpdb->prepare(
|
|
|
+ "SELECT media_id, original_file_name, description, message, active_from, active_to, reference, upload_date, download_count, last_download
|
|
|
+ FROM $table_name
|
|
|
+ WHERE media_id = %d",
|
|
|
+ $id
|
|
|
+ ));
|
|
|
+
|
|
|
+ return $updated_item;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Delete media.
|
|
|
+ */
|
|
|
+ public function delete_media($request) {
|
|
|
+ global $wpdb;
|
|
|
+
|
|
|
+ $id = $request->get_param('id');
|
|
|
+ $table_name = $wpdb->prefix . QDR_TSS_PLUGIN_TABLE;
|
|
|
+
|
|
|
+ // Get file path
|
|
|
+ $item = $wpdb->get_row($wpdb->prepare(
|
|
|
+ "SELECT id, file_path FROM $table_name WHERE media_id = %d",
|
|
|
+ $id
|
|
|
+ ));
|
|
|
+
|
|
|
+ if (!$item) {
|
|
|
+ return new WP_Error('not_found', __('Media not found.', 'qdr-temporary-shared-storage'), array('status' => 404));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Delete file
|
|
|
+ $file_path = QDR_TSS_UPLOADS_DIR . $item->file_path;
|
|
|
+ if (file_exists($file_path)) {
|
|
|
+ unlink($file_path);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Delete from database
|
|
|
+ $wpdb->delete($table_name, array('media_id' => $id), array('%d'));
|
|
|
+
|
|
|
+ return array(
|
|
|
+ 'deleted' => true,
|
|
|
+ 'media_id' => $id
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get media collection.
|
|
|
+ */
|
|
|
+ public function get_media_collection($request) {
|
|
|
+ global $wpdb;
|
|
|
+
|
|
|
+ $table_name = $wpdb->prefix . QDR_TSS_PLUGIN_TABLE;
|
|
|
+
|
|
|
+ // Parse query parameters
|
|
|
+ $params = $request->get_params();
|
|
|
+ $per_page = isset($params['per_page']) ? intval($params['per_page']) : 10;
|
|
|
+ $page = isset($params['page']) ? intval($params['page']) : 1;
|
|
|
+ $offset = ($page - 1) * $per_page;
|
|
|
+
|
|
|
+ // Build query
|
|
|
+ $query = "SELECT media_id, original_file_name, description, message, active_from, active_to, reference, upload_date, download_count, last_download, file_size
|
|
|
+ FROM $table_name";
|
|
|
+
|
|
|
+ $count_query = "SELECT COUNT(*) FROM $table_name";
|
|
|
+
|
|
|
+ $where_conditions = array();
|
|
|
+ $query_values = array();
|
|
|
+
|
|
|
+ // Filter by original_file_name
|
|
|
+ if (isset($params['original_file_name'])) {
|
|
|
+ $where_conditions[] = "original_file_name LIKE %s";
|
|
|
+ $query_values[] = '%' . $wpdb->esc_like($params['original_file_name']) . '%';
|
|
|
+ }
|
|
|
+
|
|
|
+ // Filter by reference
|
|
|
+ if (isset($params['reference'])) {
|
|
|
+ $where_conditions[] = "reference LIKE %s";
|
|
|
+ $query_values[] = '%' . $wpdb->esc_like($params['reference']) . '%';
|
|
|
+ }
|
|
|
+
|
|
|
+ // Add WHERE conditions if any
|
|
|
+ if (!empty($where_conditions)) {
|
|
|
+ $query .= " WHERE " . implode(" AND ", $where_conditions);
|
|
|
+ $count_query .= " WHERE " . implode(" AND ", $where_conditions);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Add sorting
|
|
|
+ if (isset($params['orderby'])) {
|
|
|
+ $allowed_orderby_columns = array(
|
|
|
+ 'media_id', 'original_file_name', 'description', 'active_from', 'active_to',
|
|
|
+ 'reference', 'upload_date', 'download_count', 'last_download'
|
|
|
+ );
|
|
|
+
|
|
|
+ $orderby = in_array($params['orderby'], $allowed_orderby_columns) ? $params['orderby'] : 'upload_date';
|
|
|
+ $order = (isset($params['order']) && strtoupper($params['order']) === 'ASC') ? 'ASC' : 'DESC';
|
|
|
+
|
|
|
+ $query .= " ORDER BY $orderby $order";
|
|
|
+ } else {
|
|
|
+ $query .= " ORDER BY upload_date DESC";
|
|
|
+ }
|
|
|
+
|
|
|
+ // Add pagination
|
|
|
+ $query .= " LIMIT %d OFFSET %d";
|
|
|
+ $query_values[] = $per_page;
|
|
|
+ $query_values[] = $offset;
|
|
|
+
|
|
|
+ // Prepare and execute queries
|
|
|
+ $items = $wpdb->get_results($wpdb->prepare($query, $query_values));
|
|
|
+ $total_items = $wpdb->get_var($wpdb->prepare($count_query, array_slice($query_values, 0, -2)));
|
|
|
+
|
|
|
+ // Prepare response
|
|
|
+ $response = new WP_REST_Response($items);
|
|
|
+ $response->header('X-WP-Total', $total_items);
|
|
|
+ $response->header('X-WP-TotalPages', ceil($total_items / $per_page));
|
|
|
+
|
|
|
+ return $response;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Check storage limit.
|
|
|
+ */
|
|
|
+ private function check_storage_limit($new_file_size) {
|
|
|
+ global $wpdb;
|
|
|
+
|
|
|
+ $table_name = $wpdb->prefix . QDR_TSS_PLUGIN_TABLE;
|
|
|
+
|
|
|
+ // Get total storage usage
|
|
|
+ $total_size = $wpdb->get_var("SELECT SUM(file_size) FROM $table_name");
|
|
|
+ $total_size = $total_size ? intval($total_size) : 0;
|
|
|
+
|
|
|
+ // Check if adding new file would exceed limit
|
|
|
+ return ($total_size + $new_file_size) <= $this->settings['max_upload_size'];
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Purge expired files.
|
|
|
+ */
|
|
|
+ public function purge_expired_files() {
|
|
|
+ global $wpdb;
|
|
|
+
|
|
|
+ $table_name = $wpdb->prefix . QDR_TSS_PLUGIN_TABLE;
|
|
|
+ $current_time = current_time('mysql');
|
|
|
+
|
|
|
+ // Get expired items
|
|
|
+ $expired_items = $wpdb->get_results($wpdb->prepare(
|
|
|
+ "SELECT id, media_id, file_path FROM $table_name WHERE active_to < %s",
|
|
|
+ $current_time
|
|
|
+ ));
|
|
|
+
|
|
|
+ // Delete files and database records
|
|
|
+ foreach ($expired_items as $item) {
|
|
|
+ // Delete the WordPress attachment
|
|
|
+ wp_delete_attachment($item->media_id, true);
|
|
|
+
|
|
|
+ // Delete file if it still exists (in case the WordPress function didn't remove it)
|
|
|
+ $file_path = QDR_TSS_UPLOADS_DIR . $item->file_path;
|
|
|
+ if (file_exists($file_path)) {
|
|
|
+ unlink($file_path);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Delete from our custom table
|
|
|
+ $wpdb->delete($table_name, array('id' => $item->id), array('%d'));
|
|
|
+ }
|
|
|
+
|
|
|
+ return count($expired_items);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Download shortcode.
|
|
|
+ */
|
|
|
+ public function download_shortcode($atts) {
|
|
|
+ global $wpdb;
|
|
|
+
|
|
|
+ // Extract attributes
|
|
|
+ $atts = shortcode_atts(array(
|
|
|
+ 'id' => null,
|
|
|
+ ), $atts, 'qdr_tss_download');
|
|
|
+
|
|
|
+ // Get media ID from shortcode attribute or URL parameter
|
|
|
+ $media_id = $atts['id'];
|
|
|
+ if (!$media_id && isset($_GET['id'])) {
|
|
|
+ $media_id = intval($_GET['id']);
|
|
|
+ }
|
|
|
+
|
|
|
+ // If no media ID, show form to enter media ID, password and reference
|
|
|
+ if (!$media_id) {
|
|
|
+ return $this->render_download_form();
|
|
|
+ }
|
|
|
+
|
|
|
+ $table_name = $wpdb->prefix . QDR_TSS_PLUGIN_TABLE;
|
|
|
+
|
|
|
+ // Get media information
|
|
|
+ $item = $wpdb->get_row($wpdb->prepare(
|
|
|
+ "SELECT * FROM $table_name WHERE media_id = %d",
|
|
|
+ $media_id
|
|
|
+ ));
|
|
|
+
|
|
|
+ if (!$item) {
|
|
|
+ return '<div class="qdr-tss-error">' . __('File not found.', 'qdr-temporary-shared-storage') . '</div>';
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check if file is active
|
|
|
+ $current_time = current_time('mysql');
|
|
|
+ if ($current_time < $item->active_from) {
|
|
|
+ return '<div class="qdr-tss-error">' . __('This file is not yet available for download.', 'qdr-temporary-shared-storage') . '</div>';
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($current_time > $item->active_to) {
|
|
|
+ return '<div class="qdr-tss-error">' . __('This file has expired and is no longer available for download.', 'qdr-temporary-shared-storage') . '</div>';
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check if form is submitted
|
|
|
+ if (isset($_POST['qdr_tss_download_submit'])) {
|
|
|
+ // Verify nonce
|
|
|
+ if (!isset($_POST['qdr_tss_nonce']) || !wp_verify_nonce($_POST['qdr_tss_nonce'], 'qdr_tss_download')) {
|
|
|
+ return '<div class="qdr-tss-error">' . __('Security check failed.', 'qdr-temporary-shared-storage') . '</div>';
|
|
|
+ }
|
|
|
+
|
|
|
+ // Verify password
|
|
|
+ $password = isset($_POST['qdr_tss_password']) ? $_POST['qdr_tss_password'] : '';
|
|
|
+ if (!wp_check_password($password, $item->password)) {
|
|
|
+ return '<div class="qdr-tss-error">' . __('Invalid password.', 'qdr-temporary-shared-storage') . '</div>' . $this->render_download_form($media_id);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Verify reference if set
|
|
|
+ if (!empty($item->reference)) {
|
|
|
+ $reference = isset($_POST['qdr_tss_reference']) ? $_POST['qdr_tss_reference'] : '';
|
|
|
+ if ($reference !== $item->reference) {
|
|
|
+ return '<div class="qdr-tss-error">' . __('Invalid reference.', 'qdr-temporary-shared-storage') . '</div>' . $this->render_download_form($media_id);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Update download count and last download time
|
|
|
+ $wpdb->update(
|
|
|
+ $table_name,
|
|
|
+ array(
|
|
|
+ 'download_count' => $item->download_count + 1,
|
|
|
+ 'last_download' => current_time('mysql')
|
|
|
+ ),
|
|
|
+ array('id' => $item->id),
|
|
|
+ array('%d', '%s'),
|
|
|
+ array('%d')
|
|
|
+ );
|
|
|
+
|
|
|
+ // Initiate download
|
|
|
+ $file_path = QDR_TSS_UPLOADS_DIR . $item->file_path;
|
|
|
+
|
|
|
+ if (file_exists($file_path)) {
|
|
|
+ // Set headers for download
|
|
|
+ header('Content-Description: File Transfer');
|
|
|
+ header('Content-Type: application/octet-stream');
|
|
|
+ header('Content-Disposition: attachment; filename="' . $item->original_file_name . '"');
|
|
|
+ header('Expires: 0');
|
|
|
+ header('Cache-Control: must-revalidate');
|
|
|
+ header('Pragma: public');
|
|
|
+ header('Content-Length: ' . filesize($file_path));
|
|
|
+ ob_clean();
|
|
|
+ flush();
|
|
|
+ readfile($file_path);
|
|
|
+ exit;
|
|
|
+ } else {
|
|
|
+ return '<div class="qdr-tss-error">' . __('File not found on server.', 'qdr-temporary-shared-storage') . '</div>';
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Show download form with media information
|
|
|
+ $output = '<div class="qdr-tss-download-info">';
|
|
|
+ $output .= '<h2>' . __('File Information', 'qdr-temporary-shared-storage') . '</h2>';
|
|
|
+ $output .= '<p><strong>' . __('File Name:', 'qdr-temporary-shared-storage') . '</strong> ' . esc_html($item->original_file_name) . '</p>';
|
|
|
+
|
|
|
+ if (!empty($item->description)) {
|
|
|
+ $output .= '<p><strong>' . __('Description:', 'qdr-temporary-shared-storage') . '</strong> ' . esc_html($item->description) . '</p>';
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!empty($item->message)) {
|
|
|
+ $output .= '<p><strong>' . __('Message:', 'qdr-temporary-shared-storage') . '</strong> ' . esc_html($item->message) . '</p>';
|
|
|
+ }
|
|
|
+
|
|
|
+ $output .= '</div>';
|
|
|
+ $output .= $this->render_download_form($media_id);
|
|
|
+
|
|
|
+ return $output;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Render download form.
|
|
|
+ */
|
|
|
+ private function render_download_form($media_id = null) {
|
|
|
+ $form = '<div class="qdr-tss-download-form">';
|
|
|
+ $form .= '<form method="post" action="">';
|
|
|
+
|
|
|
+ // Add nonce for security
|
|
|
+ $form .= wp_nonce_field('qdr_tss_download', 'qdr_tss_nonce', true, false);
|
|
|
+
|
|
|
+ if (!$media_id) {
|
|
|
+ $form .= '<div class="qdr-tss-form-group">';
|
|
|
+ $form .= '<label for="qdr_tss_media_id">' . __('Media ID', 'qdr-temporary-shared-storage') . '</label>';
|
|
|
+ $form .= '<input type="text" name="id" id="qdr_tss_media_id" required>';
|
|
|
+ $form .= '</div>';
|
|
|
+ } else {
|
|
|
+ $form .= '<input type="hidden" name="id" value="' . esc_attr($media_id) . '">';
|
|
|
+ }
|
|
|
+
|
|
|
+ $form .= '<div class="qdr-tss-form-group">';
|
|
|
+ $form .= '<label for="qdr_tss_password">' . __('Password', 'qdr-temporary-shared-storage') . '</label>';
|
|
|
+ $form .= '<input type="password" name="qdr_tss_password" id="qdr_tss_password" required>';
|
|
|
+ $form .= '</div>';
|
|
|
+
|
|
|
+ $form .= '<div class="qdr-tss-form-group">';
|
|
|
+ $form .= '<label for="qdr_tss_reference">' . __('Reference (if required)', 'qdr-temporary-shared-storage') . '</label>';
|
|
|
+ $form .= '<input type="text" name="qdr_tss_reference" id="qdr_tss_reference">';
|
|
|
+ $form .= '</div>';
|
|
|
+
|
|
|
+ $form .= '<div class="qdr-tss-form-group">';
|
|
|
+ $form .= '<button type="submit" name="qdr_tss_download_submit" class="qdr-tss-button">' . __('Download File', 'qdr-temporary-shared-storage') . '</button>';
|
|
|
+ $form .= '</div>';
|
|
|
+
|
|
|
+ $form .= '</form>';
|
|
|
+ $form .= '</div>';
|
|
|
+
|
|
|
+ return $form;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Initialize the plugin
|
|
|
+function qdr_temporary_shared_storage() {
|
|
|
+ return QDR_Temporary_Shared_Storage::get_instance();
|
|
|
+}
|
|
|
+qdr_temporary_shared_storage();
|
|
|
+
|
|
|
+/**
|
|
|
+ * Admin class to handle admin pages
|
|
|
+ */
|
|
|
+class QDR_TSS_Admin {
|
|
|
+ /**
|
|
|
+ * Setup the admin hooks
|
|
|
+ */
|
|
|
+ public static function init() {
|
|
|
+ add_action('wp_ajax_qdr_tss_get_items', array(__CLASS__, 'ajax_get_items'));
|
|
|
+ add_action('wp_ajax_qdr_tss_edit_item', array(__CLASS__, 'ajax_edit_item'));
|
|
|
+ add_action('wp_ajax_qdr_tss_delete_item', array(__CLASS__, 'ajax_delete_item'));
|
|
|
+ add_action('wp_ajax_qdr_tss_save_settings', array(__CLASS__, 'ajax_save_settings'));
|
|
|
+ add_action('wp_ajax_qdr_tss_purge_expired', array(__CLASS__, 'ajax_purge_expired'));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * AJAX handler for getting items
|
|
|
+ */
|
|
|
+ public static function ajax_get_items() {
|
|
|
+ // Check nonce
|
|
|
+ if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'qdr_tss_nonce')) {
|
|
|
+ wp_send_json_error(array('message' => __('Security check failed.', 'qdr-temporary-shared-storage')));
|
|
|
+ }
|
|
|
+
|
|
|
+ global $wpdb;
|
|
|
+ $table_name = $wpdb->prefix . QDR_TSS_PLUGIN_TABLE;
|
|
|
+
|
|
|
+ // Parse parameters
|
|
|
+ $per_page = isset($_POST['per_page']) ? intval($_POST['per_page']) : 10;
|
|
|
+ $page = isset($_POST['page']) ? intval($_POST['page']) : 1;
|
|
|
+ $offset = ($page - 1) * $per_page;
|
|
|
+
|
|
|
+ // Build query
|
|
|
+ $query = "SELECT * FROM $table_name";
|
|
|
+ $count_query = "SELECT COUNT(*) FROM $table_name";
|
|
|
+
|
|
|
+ $where_conditions = array();
|
|
|
+ $query_values = array();
|
|
|
+
|
|
|
+ // Search
|
|
|
+ if (isset($_POST['search']) && !empty($_POST['search'])) {
|
|
|
+ $search = $_POST['search'];
|
|
|
+ $where_conditions[] = "(original_file_name LIKE %s OR reference LIKE %s)";
|
|
|
+ $query_values[] = '%' . $wpdb->esc_like($search) . '%';
|
|
|
+ $query_values[] = '%' . $wpdb->esc_like($search) . '%';
|
|
|
+ }
|
|
|
+
|
|
|
+ // Add WHERE conditions if any
|
|
|
+ if (!empty($where_conditions)) {
|
|
|
+ $query .= " WHERE " . implode(" AND ", $where_conditions);
|
|
|
+ $count_query .= " WHERE " . implode(" AND ", $where_conditions);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Add sorting
|
|
|
+ $orderby = isset($_POST['orderby']) ? $_POST['orderby'] : 'upload_date';
|
|
|
+ $order = isset($_POST['order']) ? $_POST['order'] : 'DESC';
|
|
|
+
|
|
|
+ $allowed_columns = array(
|
|
|
+ 'media_id', 'original_file_name', 'description', 'active_from', 'active_to',
|
|
|
+ 'reference', 'upload_date', 'download_count', 'last_download'
|
|
|
+ );
|
|
|
+
|
|
|
+ if (!in_array($orderby, $allowed_columns)) {
|
|
|
+ $orderby = 'upload_date';
|
|
|
+ }
|
|
|
+
|
|
|
+ $order = strtoupper($order) === 'ASC' ? 'ASC' : 'DESC';
|
|
|
+
|
|
|
+ $query .= " ORDER BY $orderby $order";
|
|
|
+
|
|
|
+ // Add pagination
|
|
|
+ $query .= " LIMIT %d OFFSET %d";
|
|
|
+ $query_values[] = $per_page;
|
|
|
+ $query_values[] = $offset;
|
|
|
+
|
|
|
+ // Prepare and execute queries
|
|
|
+ $prepared_query = $wpdb->prepare($query, $query_values);
|
|
|
+ $items = $wpdb->get_results($prepared_query);
|
|
|
+
|
|
|
+ $prepared_count_query = count($query_values) > 2
|
|
|
+ ? $wpdb->prepare($count_query, array_slice($query_values, 0, -2))
|
|
|
+ : $count_query;
|
|
|
+
|
|
|
+ $total_items = $wpdb->get_var($prepared_count_query);
|
|
|
+
|
|
|
+ wp_send_json_success(array(
|
|
|
+ 'items' => $items,
|
|
|
+ 'total' => intval($total_items),
|
|
|
+ 'total_pages' => ceil($total_items / $per_page)
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * AJAX handler for editing an item
|
|
|
+ */
|
|
|
+ public static function ajax_edit_item() {
|
|
|
+ // Check nonce
|
|
|
+ if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'qdr_tss_nonce')) {
|
|
|
+ wp_send_json_error(array('message' => __('Security check failed.', 'qdr-temporary-shared-storage')));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check required parameters
|
|
|
+ if (!isset($_POST['id']) || empty($_POST['id'])) {
|
|
|
+ wp_send_json_error(array('message' => __('Missing item ID.', 'qdr-temporary-shared-storage')));
|
|
|
+ }
|
|
|
+
|
|
|
+ global $wpdb;
|
|
|
+ $table_name = $wpdb->prefix . QDR_TSS_PLUGIN_TABLE;
|
|
|
+ $id = intval($_POST['id']);
|
|
|
+
|
|
|
+ // Prepare update data
|
|
|
+ $update_data = array();
|
|
|
+ $update_format = array();
|
|
|
+
|
|
|
+ if (isset($_POST['description'])) {
|
|
|
+ $update_data['description'] = sanitize_textarea_field($_POST['description']);
|
|
|
+ $update_format[] = '%s';
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isset($_POST['message'])) {
|
|
|
+ $update_data['message'] = sanitize_textarea_field($_POST['message']);
|
|
|
+ $update_format[] = '%s';
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isset($_POST['active_from'])) {
|
|
|
+ $update_data['active_from'] = sanitize_text_field($_POST['active_from']);
|
|
|
+ $update_format[] = '%s';
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isset($_POST['active_to'])) {
|
|
|
+ $update_data['active_to'] = sanitize_text_field($_POST['active_to']);
|
|
|
+ $update_format[] = '%s';
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isset($_POST['reference'])) {
|
|
|
+ $update_data['reference'] = sanitize_text_field($_POST['reference']);
|
|
|
+ $update_format[] = '%s';
|
|
|
+ }
|
|
|
+
|
|
|
+ if (empty($update_data)) {
|
|
|
+ wp_send_json_error(array('message' => __('No changes to update.', 'qdr-temporary-shared-storage')));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Update in database
|
|
|
+ $result = $wpdb->update(
|
|
|
+ $table_name,
|
|
|
+ $update_data,
|
|
|
+ array('id' => $id),
|
|
|
+ $update_format,
|
|
|
+ array('%d')
|
|
|
+ );
|
|
|
+
|
|
|
+ if ($result === false) {
|
|
|
+ wp_send_json_error(array('message' => __('Failed to update item.', 'qdr-temporary-shared-storage')));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Get updated record
|
|
|
+ $item = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table_name WHERE id = %d", $id));
|
|
|
+
|
|
|
+ wp_send_json_success(array(
|
|
|
+ 'item' => $item,
|
|
|
+ 'message' => __('Item updated successfully.', 'qdr-temporary-shared-storage')
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * AJAX handler for deleting an item
|
|
|
+ */
|
|
|
+ public static function ajax_delete_item() {
|
|
|
+ // Check nonce
|
|
|
+ if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'qdr_tss_nonce')) {
|
|
|
+ wp_send_json_error(array('message' => __('Security check failed.', 'qdr-temporary-shared-storage')));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check required parameters
|
|
|
+ if (!isset($_POST['id']) || empty($_POST['id'])) {
|
|
|
+ wp_send_json_error(array('message' => __('Missing item ID.', 'qdr-temporary-shared-storage')));
|
|
|
+ }
|
|
|
+
|
|
|
+ global $wpdb;
|
|
|
+ $table_name = $wpdb->prefix . QDR_TSS_PLUGIN_TABLE;
|
|
|
+ $id = intval($_POST['id']);
|
|
|
+
|
|
|
+ // Get file path
|
|
|
+ $item = $wpdb->get_row($wpdb->prepare(
|
|
|
+ "SELECT file_path FROM $table_name WHERE id = %d",
|
|
|
+ $id
|
|
|
+ ));
|
|
|
+
|
|
|
+ if (!$item) {
|
|
|
+ wp_send_json_error(array('message' => __('Item not found.', 'qdr-temporary-shared-storage')));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Delete file
|
|
|
+ $file_path = QDR_TSS_UPLOADS_DIR . $item->file_path;
|
|
|
+ if (file_exists($file_path)) {
|
|
|
+ unlink($file_path);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Delete from database
|
|
|
+ $result = $wpdb->delete($table_name, array('id' => $id), array('%d'));
|
|
|
+
|
|
|
+ if ($result === false) {
|
|
|
+ wp_send_json_error(array('message' => __('Failed to delete item.', 'qdr-temporary-shared-storage')));
|
|
|
+ }
|
|
|
+
|
|
|
+ wp_send_json_success(array(
|
|
|
+ 'message' => __('Item deleted successfully.', 'qdr-temporary-shared-storage')
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * AJAX handler for saving settings
|
|
|
+ */
|
|
|
+ public static function ajax_save_settings() {
|
|
|
+ // Check nonce
|
|
|
+ if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'qdr_tss_nonce')) {
|
|
|
+ wp_send_json_error(array('message' => __('Security check failed.', 'qdr-temporary-shared-storage')));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Get current settings
|
|
|
+ $settings = get_option('qdr_tss_settings', array());
|
|
|
+
|
|
|
+ // Update settings
|
|
|
+ if (isset($_POST['api_key'])) {
|
|
|
+ $settings['api_key'] = sanitize_text_field($_POST['api_key']);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isset($_POST['media_category'])) {
|
|
|
+ $settings['media_category'] = sanitize_text_field($_POST['media_category']);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isset($_POST['max_upload_size'])) {
|
|
|
+ $settings['max_upload_size'] = intval($_POST['max_upload_size']);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Save settings
|
|
|
+ update_option('qdr_tss_settings', $settings);
|
|
|
+
|
|
|
+ wp_send_json_success(array(
|
|
|
+ 'message' => __('Settings saved successfully.', 'qdr-temporary-shared-storage')
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * AJAX handler for purging expired files
|
|
|
+ */
|
|
|
+ public static function ajax_purge_expired() {
|
|
|
+ // Check nonce
|
|
|
+ if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'qdr_tss_nonce')) {
|
|
|
+ wp_send_json_error(array('message' => __('Security check failed.', 'qdr-temporary-shared-storage')));
|
|
|
+ }
|
|
|
+
|
|
|
+ $plugin = qdr_temporary_shared_storage();
|
|
|
+ $count = $plugin->purge_expired_files();
|
|
|
+
|
|
|
+ wp_send_json_success(array(
|
|
|
+ 'message' => sprintf(
|
|
|
+ _n(
|
|
|
+ '%d expired file has been purged.',
|
|
|
+ '%d expired files have been purged.',
|
|
|
+ $count,
|
|
|
+ 'qdr-temporary-shared-storage'
|
|
|
+ ),
|
|
|
+ $count
|
|
|
+ )
|
|
|
+ ));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Initialize admin class
|
|
|
+QDR_TSS_Admin::init();
|
|
|
+
|
|
|
+// Include admin pages
|
|
|
+require_once QDR_TSS_PLUGIN_DIR . 'admin/media-page.php';
|
|
|
+require_once QDR_TSS_PLUGIN_DIR . 'admin/settings-page.php';
|
|
|
+
|