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\nOrder Allow,Deny\nDeny from all\n"; 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\d+)', array( 'methods' => 'GET', 'callback' => array($this, 'get_media'), 'permission_callback' => array($this, 'check_rest_permission') )); register_rest_route('qdr-tss/v1', '/media/(?P\d+)', array( 'methods' => 'PUT', 'callback' => array($this, 'update_media'), 'permission_callback' => array($this, 'check_rest_permission') )); register_rest_route('qdr-tss/v1', '/media/(?P\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 '
' . __('File not found.', 'qdr-temporary-shared-storage') . '
'; } // Check if file is active $current_time = current_time('mysql'); if ($current_time < $item->active_from) { return '
' . __('This file is not yet available for download.', 'qdr-temporary-shared-storage') . '
'; } if ($current_time > $item->active_to) { return '
' . __('This file has expired and is no longer available for download.', 'qdr-temporary-shared-storage') . '
'; } // 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 '
' . __('Security check failed.', 'qdr-temporary-shared-storage') . '
'; } // Verify password $password = isset($_POST['qdr_tss_password']) ? $_POST['qdr_tss_password'] : ''; if (!wp_check_password($password, $item->password)) { return '
' . __('Invalid password.', 'qdr-temporary-shared-storage') . '
' . $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 '
' . __('Invalid reference.', 'qdr-temporary-shared-storage') . '
' . $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 '
' . __('File not found on server.', 'qdr-temporary-shared-storage') . '
'; } } // Show download form with media information $output = '
'; $output .= '

' . __('File Information', 'qdr-temporary-shared-storage') . '

'; $output .= '

' . __('File Name:', 'qdr-temporary-shared-storage') . ' ' . esc_html($item->original_file_name) . '

'; if (!empty($item->description)) { $output .= '

' . __('Description:', 'qdr-temporary-shared-storage') . ' ' . esc_html($item->description) . '

'; } if (!empty($item->message)) { $output .= '

' . __('Message:', 'qdr-temporary-shared-storage') . ' ' . esc_html($item->message) . '

'; } $output .= '
'; $output .= $this->render_download_form($media_id); return $output; } /** * Render download form. */ private function render_download_form($media_id = null) { $form = '
'; $form .= '
'; // Add nonce for security $form .= wp_nonce_field('qdr_tss_download', 'qdr_tss_nonce', true, false); if (!$media_id) { $form .= '
'; $form .= ''; $form .= ''; $form .= '
'; } else { $form .= ''; } $form .= '
'; $form .= ''; $form .= ''; $form .= '
'; $form .= '
'; $form .= ''; $form .= ''; $form .= '
'; $form .= '
'; $form .= ''; $form .= '
'; $form .= '
'; $form .= '
'; 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';