| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235 |
- <?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';
-
|