ソースを参照

qdr-temporary-shared-storage: add initial version 1.0.0

Dalibor Votruba 1 年間 前
コミット
4ee6acfe98

+ 704 - 0
qdr-temporary-shared-storage/admin/media-page.php

@@ -0,0 +1,704 @@
+<?php
+/**
+ * Admin Media Page for QDR Temporary Shared Storage
+ *
+ * @package QDR_Temporary_Shared_Storage
+ */
+
+// If this file is called directly, abort.
+if (!defined('WPINC')) {
+    die;
+}
+
+/**
+ * Render the Media | Shared Storage admin page
+ */
+function qdr_tss_render_media_page() {
+    // Get current settings
+    $settings = get_option('qdr_tss_settings', array(
+        'api_key' => '',
+        'media_category' => 'shared-storage',
+        'max_upload_size' => 1073741824, // 1GB default
+    ));
+    
+    // Get total storage usage
+    global $wpdb;
+    $table_name = $wpdb->prefix . QDR_TSS_PLUGIN_TABLE;
+    $total_size = $wpdb->get_var("SELECT SUM(file_size) FROM $table_name");
+    $total_size = $total_size ? intval($total_size) : 0;
+    
+    $max_size = isset($settings['max_upload_size']) ? intval($settings['max_upload_size']) : 1073741824;
+    $usage_percent = ($max_size > 0) ? min(100, ($total_size / $max_size) * 100) : 0;
+    ?>
+    <div class="wrap qdr-tss-admin">
+        <h1><?php esc_html_e('Shared Storage', 'qdr-temporary-shared-storage'); ?></h1>
+        
+        <div class="qdr-tss-storage-summary">
+            <h2><?php esc_html_e('Storage Usage', 'qdr-temporary-shared-storage'); ?></h2>
+            <div class="qdr-tss-storage-bar">
+                <div class="qdr-tss-storage-used" style="width: <?php echo esc_attr($usage_percent); ?>%"></div>
+            </div>
+            <p>
+                <?php echo esc_html(qdr_tss_format_bytes($total_size)); ?> / <?php echo esc_html(qdr_tss_format_bytes($max_size)); ?>
+                (<?php echo number_format($usage_percent, 1); ?>%)
+            </p>
+        </div>
+        
+        <div class="qdr-tss-toolbar">
+            <div class="qdr-tss-search">
+                <input type="text" id="qdr-tss-search-filename" placeholder="<?php esc_attr_e('Search by filename', 'qdr-temporary-shared-storage'); ?>">
+                <input type="text" id="qdr-tss-search-reference" placeholder="<?php esc_attr_e('Search by reference', 'qdr-temporary-shared-storage'); ?>">
+                <button class="button" id="qdr-tss-search-button">
+                    <span class="dashicons dashicons-search"></span> <?php esc_html_e('Search', 'qdr-temporary-shared-storage'); ?>
+                </button>
+                <button class="button" id="qdr-tss-reset-search">
+                    <span class="dashicons dashicons-dismiss"></span> <?php esc_html_e('Reset', 'qdr-temporary-shared-storage'); ?>
+                </button>
+            </div>
+            
+            <div class="qdr-tss-actions">
+                <button class="button button-primary" id="qdr-tss-refresh">
+                    <span class="dashicons dashicons-update"></span> <?php esc_html_e('Refresh', 'qdr-temporary-shared-storage'); ?>
+                </button>
+            </div>
+        </div>
+        
+        <div class="qdr-tss-table-container">
+            <table class="wp-list-table widefat fixed striped qdr-tss-table">
+                <thead>
+                    <tr>
+                        <th class="column-media-id sortable" data-sort="media_id"><?php esc_html_e('Media ID', 'qdr-temporary-shared-storage'); ?> <span class="sorting-indicator"></span></th>
+                        <th class="column-filename sortable" data-sort="original_file_name"><?php esc_html_e('File Name', 'qdr-temporary-shared-storage'); ?> <span class="sorting-indicator"></span></th>
+                        <th class="column-description"><?php esc_html_e('Description', 'qdr-temporary-shared-storage'); ?></th>
+                        <th class="column-message"><?php esc_html_e('Message', 'qdr-temporary-shared-storage'); ?></th>
+                        <th class="column-active-from sortable" data-sort="active_from"><?php esc_html_e('Active From', 'qdr-temporary-shared-storage'); ?> <span class="sorting-indicator"></span></th>
+                        <th class="column-active-to sortable" data-sort="active_to"><?php esc_html_e('Active To', 'qdr-temporary-shared-storage'); ?> <span class="sorting-indicator"></span></th>
+                        <th class="column-reference sortable" data-sort="reference"><?php esc_html_e('Reference', 'qdr-temporary-shared-storage'); ?> <span class="sorting-indicator"></span></th>
+                        <th class="column-upload-date sortable" data-sort="upload_date"><?php esc_html_e('Upload Date', 'qdr-temporary-shared-storage'); ?> <span class="sorting-indicator"></span></th>
+                        <th class="column-download-count sortable" data-sort="download_count"><?php esc_html_e('Downloads', 'qdr-temporary-shared-storage'); ?> <span class="sorting-indicator"></span></th>
+                        <th class="column-last-download sortable" data-sort="last_download"><?php esc_html_e('Last Download', 'qdr-temporary-shared-storage'); ?> <span class="sorting-indicator"></span></th>
+                        <th class="column-actions"><?php esc_html_e('Actions', 'qdr-temporary-shared-storage'); ?></th>
+                    </tr>
+                </thead>
+                <tbody id="qdr-tss-items-list">
+                    <tr>
+                        <td colspan="11" class="qdr-tss-loading">
+                            <span class="spinner is-active"></span> <?php esc_html_e('Loading...', 'qdr-temporary-shared-storage'); ?>
+                        </td>
+                    </tr>
+                </tbody>
+            </table>
+        </div>
+        
+        <div class="qdr-tss-pagination">
+            <div class="qdr-tss-pagination-info"></div>
+            <div class="qdr-tss-pagination-controls"></div>
+        </div>
+    </div>
+    
+    <!-- Edit Item Modal -->
+    <div id="qdr-tss-edit-modal" class="qdr-tss-modal">
+        <div class="qdr-tss-modal-content">
+            <span class="qdr-tss-modal-close">&times;</span>
+            <h2><?php esc_html_e('Edit Shared File', 'qdr-temporary-shared-storage'); ?></h2>
+            
+            <form id="qdr-tss-edit-form">
+                <input type="hidden" id="qdr-tss-edit-id" name="id">
+                
+                <div class="qdr-tss-form-row">
+                    <label for="qdr-tss-edit-description"><?php esc_html_e('Description', 'qdr-temporary-shared-storage'); ?></label>
+                    <textarea id="qdr-tss-edit-description" name="description" rows="3"></textarea>
+                </div>
+                
+                <div class="qdr-tss-form-row">
+                    <label for="qdr-tss-edit-message"><?php esc_html_e('Message', 'qdr-temporary-shared-storage'); ?></label>
+                    <textarea id="qdr-tss-edit-message" name="message" rows="3"></textarea>
+                </div>
+                
+                <div class="qdr-tss-form-row">
+                    <label for="qdr-tss-edit-active-from"><?php esc_html_e('Active From', 'qdr-temporary-shared-storage'); ?></label>
+                    <input type="text" id="qdr-tss-edit-active-from" name="active_from" class="qdr-tss-datepicker" autocomplete="off">
+                </div>
+                
+                <div class="qdr-tss-form-row">
+                    <label for="qdr-tss-edit-active-to"><?php esc_html_e('Active To', 'qdr-temporary-shared-storage'); ?></label>
+                    <input type="text" id="qdr-tss-edit-active-to" name="active_to" class="qdr-tss-datepicker" autocomplete="off">
+                </div>
+                
+                <div class="qdr-tss-form-row">
+                    <label for="qdr-tss-edit-reference"><?php esc_html_e('Reference', 'qdr-temporary-shared-storage'); ?></label>
+                    <input type="text" id="qdr-tss-edit-reference" name="reference">
+                </div>
+                
+                <div class="qdr-tss-form-actions">
+                    <button type="submit" class="button button-primary"><?php esc_html_e('Save Changes', 'qdr-temporary-shared-storage'); ?></button>
+                    <button type="button" class="button qdr-tss-modal-cancel"><?php esc_html_e('Cancel', 'qdr-temporary-shared-storage'); ?></button>
+                </div>
+            </form>
+            
+            <div id="qdr-tss-edit-status"></div>
+        </div>
+    </div>
+    
+    <!-- View Details Modal -->
+    <div id="qdr-tss-details-modal" class="qdr-tss-modal">
+        <div class="qdr-tss-modal-content">
+            <span class="qdr-tss-modal-close">&times;</span>
+            <h2><?php esc_html_e('File Details', 'qdr-temporary-shared-storage'); ?></h2>
+            
+            <div class="qdr-tss-details-content">
+                <table class="widefat striped">
+                    <tr>
+                        <th><?php esc_html_e('Media ID', 'qdr-temporary-shared-storage'); ?></th>
+                        <td id="qdr-tss-details-media-id"></td>
+                    </tr>
+                    <tr>
+                        <th><?php esc_html_e('File Name', 'qdr-temporary-shared-storage'); ?></th>
+                        <td id="qdr-tss-details-filename"></td>
+                    </tr>
+                    <tr>
+                        <th><?php esc_html_e('Description', 'qdr-temporary-shared-storage'); ?></th>
+                        <td id="qdr-tss-details-description"></td>
+                    </tr>
+                    <tr>
+                        <th><?php esc_html_e('Message', 'qdr-temporary-shared-storage'); ?></th>
+                        <td id="qdr-tss-details-message"></td>
+                    </tr>
+                    <tr>
+                        <th><?php esc_html_e('Reference', 'qdr-temporary-shared-storage'); ?></th>
+                        <td id="qdr-tss-details-reference"></td>
+                    </tr>
+                    <tr>
+                        <th><?php esc_html_e('Active Period', 'qdr-temporary-shared-storage'); ?></th>
+                        <td id="qdr-tss-details-active-period"></td>
+                    </tr>
+                    <tr>
+                        <th><?php esc_html_e('File Size', 'qdr-temporary-shared-storage'); ?></th>
+                        <td id="qdr-tss-details-filesize"></td>
+                    </tr>
+                    <tr>
+                        <th><?php esc_html_e('Download Count', 'qdr-temporary-shared-storage'); ?></th>
+                        <td id="qdr-tss-details-download-count"></td>
+                    </tr>
+                    <tr>
+                        <th><?php esc_html_e('Last Download', 'qdr-temporary-shared-storage'); ?></th>
+                        <td id="qdr-tss-details-last-download"></td>
+                    </tr>
+                    <tr>
+                        <th><?php esc_html_e('Permalink', 'qdr-temporary-shared-storage'); ?></th>
+                        <td id="qdr-tss-details-permalink"></td>
+                    </tr>
+                    <tr>
+                        <th><?php esc_html_e('Shortcode', 'qdr-temporary-shared-storage'); ?></th>
+                        <td id="qdr-tss-details-shortcode"></td>
+                    </tr>
+                </table>
+            </div>
+            
+            <div class="qdr-tss-form-actions">
+                <button type="button" class="button button-secondary qdr-tss-copy-shortcode"><?php esc_html_e('Copy Shortcode', 'qdr-temporary-shared-storage'); ?></button>
+                <button type="button" class="button button-secondary qdr-tss-copy-permalink"><?php esc_html_e('Copy Permalink', 'qdr-temporary-shared-storage'); ?></button>
+                <button type="button" class="button qdr-tss-modal-cancel"><?php esc_html_e('Close', 'qdr-temporary-shared-storage'); ?></button>
+            </div>
+        </div>
+    </div>
+    
+    <!-- Confirm Delete Modal -->
+    <div id="qdr-tss-confirm-delete-modal" class="qdr-tss-modal">
+        <div class="qdr-tss-modal-content qdr-tss-modal-sm">
+            <span class="qdr-tss-modal-close">&times;</span>
+            <h2><?php esc_html_e('Confirm Delete', 'qdr-temporary-shared-storage'); ?></h2>
+            
+            <p><?php esc_html_e('Are you sure you want to delete this file? This action cannot be undone.', 'qdr-temporary-shared-storage'); ?></p>
+            
+            <div class="qdr-tss-form-actions">
+                <button type="button" class="button button-primary qdr-tss-confirm-delete-button"><?php esc_html_e('Delete', 'qdr-temporary-shared-storage'); ?></button>
+                <button type="button" class="button qdr-tss-modal-cancel"><?php esc_html_e('Cancel', 'qdr-temporary-shared-storage'); ?></button>
+            </div>
+            
+            <input type="hidden" id="qdr-tss-delete-id">
+        </div>
+    </div>
+    
+    <script type="text/javascript">
+        jQuery(document).ready(function($) {
+            var currentPage = 1;
+            var perPage = 10;
+            var totalPages = 1;
+            var currentOrderBy = 'upload_date';
+            var currentOrder = 'DESC';
+            var currentSearchFilename = '';
+            var currentSearchReference = '';
+            
+            // Load items
+            function loadItems() {
+                var data = {
+                    action: 'qdr_tss_get_items',
+                    nonce: qdr_tss.nonce,
+                    page: currentPage,
+                    per_page: perPage,
+                    orderby: currentOrderBy,
+                    order: currentOrder,
+                    original_file_name: currentSearchFilename,
+                    reference: currentSearchReference
+                };
+                
+                // Show loading indicator
+                $('#qdr-tss-items-list').html('<tr><td colspan="11" class="qdr-tss-loading"><span class="spinner is-active"></span> <?php esc_html_e('Loading...', 'qdr-temporary-shared-storage'); ?></td></tr>');
+                
+                $.post(ajaxurl, data, function(response) {
+                    if (response.success) {
+                        renderItems(response.data.items);
+                        renderPagination(response.data.total, response.data.total_pages);
+                    } else {
+                        alert(response.data.message || '<?php esc_html_e('Error loading items.', 'qdr-temporary-shared-storage'); ?>');
+                    }
+                }).fail(function() {
+                    $('#qdr-tss-items-list').html('<tr><td colspan="11"><?php esc_html_e('Error loading items. Please try again.', 'qdr-temporary-shared-storage'); ?></td></tr>');
+                });
+            }
+            
+            // Render items
+            function renderItems(items) {
+                var $tbody = $('#qdr-tss-items-list');
+                $tbody.empty();
+                
+                if (!items || items.length === 0) {
+                    $tbody.append('<tr><td colspan="11"><?php esc_html_e('No items found.', 'qdr-temporary-shared-storage'); ?></td></tr>');
+                    return;
+                }
+                
+                $.each(items, function(index, item) {
+                    var statusClass = '';
+                    var now = new Date();
+                    var activeFrom = new Date(item.active_from);
+                    var activeTo = new Date(item.active_to);
+                    
+                    if (now < activeFrom) {
+                        statusClass = 'qdr-tss-status-pending';
+                    } else if (now > activeTo) {
+                        statusClass = 'qdr-tss-status-expired';
+                    } else {
+                        statusClass = 'qdr-tss-status-active';
+                    }
+                    
+                    var row = '<tr data-id="' + item.id + '" class="' + statusClass + '">';
+                    
+                    row += '<td>' + item.media_id + '</td>';
+                    row += '<td>' + escapeHtml(item.original_file_name) + '</td>';
+                    row += '<td>' + escapeHtml(item.description || '') + '</td>';
+                    row += '<td>' + escapeHtml(item.message || '') + '</td>';
+                    row += '<td>' + formatDate(item.active_from) + '</td>';
+                    row += '<td>' + formatDate(item.active_to) + '</td>';
+                    row += '<td>' + escapeHtml(item.reference || '') + '</td>';
+                    row += '<td>' + formatDate(item.upload_date) + '</td>';
+                    row += '<td>' + item.download_count + '</td>';
+                    row += '<td>' + (item.last_download ? formatDate(item.last_download) : '-') + '</td>';
+                    
+                    row += '<td class="column-actions">';
+                    row += '<div class="qdr-tss-action-buttons">';
+                    row += '<button class="button qdr-tss-view-button" data-id="' + item.id + '" title="<?php esc_attr_e('View Details', 'qdr-temporary-shared-storage'); ?>"><span class="dashicons dashicons-visibility"></span></button>';
+                    row += '<button class="button qdr-tss-edit-button" data-id="' + item.id + '" title="<?php esc_attr_e('Edit', 'qdr-temporary-shared-storage'); ?>"><span class="dashicons dashicons-edit"></span></button>';
+                    row += '<button class="button qdr-tss-delete-button" data-id="' + item.id + '" title="<?php esc_attr_e('Delete', 'qdr-temporary-shared-storage'); ?>"><span class="dashicons dashicons-trash"></span></button>';
+                    row += '</div>';
+                    row += '</td>';
+                    
+                    row += '</tr>';
+                    
+                    $tbody.append(row);
+                });
+                
+                // Initialize action buttons
+                initActionButtons();
+                
+                // Update sorting indicators
+                updateSortIndicators();
+            }
+            
+            // Initialize action buttons
+            function initActionButtons() {
+                // View button
+                $('.qdr-tss-view-button').on('click', function() {
+                    var id = $(this).data('id');
+                    viewItem(id);
+                });
+                
+                // Edit button
+                $('.qdr-tss-edit-button').on('click', function() {
+                    var id = $(this).data('id');
+                    editItem(id);
+                });
+                
+                // Delete button
+                $('.qdr-tss-delete-button').on('click', function() {
+                    var id = $(this).data('id');
+                    showDeleteConfirmation(id);
+                });
+            }
+            
+            // Format date
+            function formatDate(dateString) {
+                if (!dateString || dateString === '0000-00-00 00:00:00') {
+                    return '-';
+                }
+                
+                var date = new Date(dateString);
+                
+                // Check if date is valid
+                if (isNaN(date.getTime())) {
+                    return dateString;
+                }
+                
+                return date.toLocaleDateString() + ' ' + date.toLocaleTimeString();
+            }
+            
+            // Format file size
+            function formatFileSize(bytes) {
+                if (bytes === 0) return '0 Bytes';
+                
+                var k = 1024;
+                var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
+                var i = Math.floor(Math.log(bytes) / Math.log(k));
+                
+                return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
+            }
+            
+            // Escape HTML
+            function escapeHtml(text) {
+                if (!text) return '';
+                
+                var map = {
+                    '&': '&amp;',
+                    '<': '&lt;',
+                    '>': '&gt;',
+                    '"': '&quot;',
+                    "'": '&#039;'
+                };
+                
+                return text.toString().replace(/[&<>"']/g, function(m) { return map[m]; });
+            }
+            
+            // Render pagination
+            function renderPagination(total, totalPages) {
+                var $info = $('.qdr-tss-pagination-info');
+                var $controls = $('.qdr-tss-pagination-controls');
+                
+                // Update info
+                var startItem = (currentPage - 1) * perPage + 1;
+                var endItem = Math.min(currentPage * perPage, total);
+                
+                if (total === 0) {
+                    $info.html('<?php esc_html_e('No items', 'qdr-temporary-shared-storage'); ?>');
+                    $controls.empty();
+                    return;
+                }
+                
+                $info.html('<?php esc_html_e('Showing', 'qdr-temporary-shared-storage'); ?> ' + startItem + ' - ' + endItem + ' <?php esc_html_e('of', 'qdr-temporary-shared-storage'); ?> ' + total);
+                
+                // Update controls
+                $controls.empty();
+                
+                if (totalPages <= 1) {
+                    return;
+                }
+                
+                // First page
+                if (currentPage > 1) {
+                    $controls.append('<button class="button qdr-tss-page-button" data-page="1" title="<?php esc_attr_e('First Page', 'qdr-temporary-shared-storage'); ?>">&laquo;</button>');
+                }
+                
+                // Previous page
+                if (currentPage > 1) {
+                    $controls.append('<button class="button qdr-tss-page-button" data-page="' + (currentPage - 1) + '" title="<?php esc_attr_e('Previous Page', 'qdr-temporary-shared-storage'); ?>">&lsaquo;</button>');
+                }
+                
+                // Page numbers
+                var startPage = Math.max(1, currentPage - 2);
+                var endPage = Math.min(totalPages, startPage + 4);
+                
+                if (endPage - startPage < 4) {
+                    startPage = Math.max(1, endPage - 4);
+                }
+                
+                for (var i = startPage; i <= endPage; i++) {
+                    var activeClass = i === currentPage ? 'button-primary' : '';
+                    $controls.append('<button class="button qdr-tss-page-button ' + activeClass + '" data-page="' + i + '">' + i + '</button>');
+                }
+                
+                // Next page
+                if (currentPage < totalPages) {
+                    $controls.append('<button class="button qdr-tss-page-button" data-page="' + (currentPage + 1) + '" title="<?php esc_attr_e('Next Page', 'qdr-temporary-shared-storage'); ?>">&rsaquo;</button>');
+                }
+                
+                // Last page
+                if (currentPage < totalPages) {
+                    $controls.append('<button class="button qdr-tss-page-button" data-page="' + totalPages + '" title="<?php esc_attr_e('Last Page', 'qdr-temporary-shared-storage'); ?>">&raquo;</button>');
+                }
+                
+                // Bind click event to page buttons
+                $('.qdr-tss-page-button').on('click', function() {
+                    currentPage = parseInt($(this).data('page'));
+                    loadItems();
+                });
+            }
+            
+            // Update sort indicators
+            function updateSortIndicators() {
+                // Remove all indicators
+                $('.qdr-tss-table th').removeClass('sorted asc desc');
+                
+                // Add indicator to current sort column
+                var $th = $('.qdr-tss-table th[data-sort="' + currentOrderBy + '"]');
+                $th.addClass('sorted ' + currentOrder.toLowerCase());
+            }
+            
+            // View item details
+            function viewItem(id) {
+                // Find item in the table
+                var $row = $('tr[data-id="' + id + '"]');
+                if ($row.length === 0) return;
+                
+                var $cells = $row.find('td');
+                
+                var mediaId = $cells.eq(0).text();
+                var fileName = $cells.eq(1).text();
+                var description = $cells.eq(2).text();
+                var message = $cells.eq(3).text();
+                var activeFrom = $cells.eq(4).text();
+                var activeTo = $cells.eq(5).text();
+                var reference = $cells.eq(6).text();
+                var uploadDate = $cells.eq(7).text();
+                var downloadCount = $cells.eq(8).text();
+                var lastDownload = $cells.eq(9).text();
+                
+                // Get additional data via AJAX
+                var data = {
+                    action: 'qdr_tss_get_item_details',
+                    nonce: qdr_tss.nonce,
+                    id: id,
+                    media_id: mediaId
+                };
+                
+                $.post(ajaxurl, data, function(response) {
+                    if (response.success) {
+                        var item = response.data.item;
+                        
+                        // Populate details
+                        $('#qdr-tss-details-media-id').text(mediaId);
+                        $('#qdr-tss-details-filename').text(fileName);
+                        $('#qdr-tss-details-description').text(description || '-');
+                        $('#qdr-tss-details-message').text(message || '-');
+                        $('#qdr-tss-details-reference').text(reference || '-');
+                        $('#qdr-tss-details-active-period').text(activeFrom + ' - ' + activeTo);
+                        $('#qdr-tss-details-filesize').text(formatFileSize(item.file_size));
+                        $('#qdr-tss-details-download-count').text(downloadCount);
+                        $('#qdr-tss-details-last-download').text(lastDownload);
+                        
+                        // Permalink and shortcode
+                        var permalink = '<?php echo esc_url(site_url('shared-file-download/')); ?>' + mediaId;
+                        var shortcode = '[qdr_tss_download id="' + mediaId + '"]';
+                        
+                        $('#qdr-tss-details-permalink').html('<a href="' + permalink + '" target="_blank">' + permalink + '</a>');
+                        $('#qdr-tss-details-shortcode').html('<code>' + shortcode + '</code>');
+                        
+                        // Show modal
+                        $('#qdr-tss-details-modal').show();
+                    } else {
+                        alert(response.data.message || '<?php esc_html_e('Error loading item details.', 'qdr-temporary-shared-storage'); ?>');
+                    }
+                }).fail(function() {
+                    alert('<?php esc_html_e('Error loading item details. Please try again.', 'qdr-temporary-shared-storage'); ?>');
+                });
+            }
+            
+            // Edit item
+            function editItem(id) {
+                // Find item in the table
+                var $row = $('tr[data-id="' + id + '"]');
+                if ($row.length === 0) return;
+                
+                var $cells = $row.find('td');
+                
+                // Populate form fields
+                $('#qdr-tss-edit-id').val(id);
+                $('#qdr-tss-edit-description').val($cells.eq(2).text());
+                $('#qdr-tss-edit-message').val($cells.eq(3).text());
+                
+                // Parse dates for datepicker
+                var activeFrom = $cells.eq(4).text();
+                var activeTo = $cells.eq(5).text();
+                
+                $('#qdr-tss-edit-active-from').val(activeFrom !== '-' ? activeFrom : '');
+                $('#qdr-tss-edit-active-to').val(activeTo !== '-' ? activeTo : '');
+                $('#qdr-tss-edit-reference').val($cells.eq(6).text());
+                
+                // Clear status
+                $('#qdr-tss-edit-status').html('');
+                
+                // Show modal
+                $('#qdr-tss-edit-modal').show();
+                
+                // Initialize datepickers
+                $('.qdr-tss-datepicker').datepicker({
+                    dateFormat: 'yy-mm-dd',
+                    changeMonth: true,
+                    changeYear: true
+                });
+            }
+            
+            // Show delete confirmation
+            function showDeleteConfirmation(id) {
+                $('#qdr-tss-delete-id').val(id);
+                $('#qdr-tss-confirm-delete-modal').show();
+            }
+            
+            // Delete item
+            function deleteItem(id) {
+                var data = {
+                    action: 'qdr_tss_delete_item',
+                    nonce: qdr_tss.nonce,
+                    id: id
+                };
+                
+                $.post(ajaxurl, data, function(response) {
+                    if (response.success) {
+                        // Hide modal
+                        $('#qdr-tss-confirm-delete-modal').hide();
+                        
+                        // Show success message
+                        alert(response.data.message || '<?php esc_html_e('File deleted successfully.', 'qdr-temporary-shared-storage'); ?>');
+                        
+                        // Reload items
+                        loadItems();
+                    } else {
+                        alert(response.data.message || '<?php esc_html_e('Error deleting file.', 'qdr-temporary-shared-storage'); ?>');
+                    }
+                }).fail(function() {
+                    alert('<?php esc_html_e('Error deleting file. Please try again.', 'qdr-temporary-shared-storage'); ?>');
+                });
+            }
+            
+            // Copy text to clipboard
+            function copyToClipboard(text) {
+                var $temp = $('<input>');
+                $('body').append($temp);
+                $temp.val(text).select();
+                document.execCommand('copy');
+                $temp.remove();
+            }
+            
+            // Handle form submission
+            $('#qdr-tss-edit-form').on('submit', function(e) {
+                e.preventDefault();
+                
+                // Show loading status
+                $('#qdr-tss-edit-status').html('<div class="qdr-tss-loading"><span class="spinner is-active"></span> <?php esc_html_e('Saving...', 'qdr-temporary-shared-storage'); ?></div>');
+                
+                var data = {
+                    action: 'qdr_tss_edit_item',
+                    nonce: qdr_tss.nonce,
+                    id: $('#qdr-tss-edit-id').val(),
+                    description: $('#qdr-tss-edit-description').val(),
+                    message: $('#qdr-tss-edit-message').val(),
+                    active_from: $('#qdr-tss-edit-active-from').val(),
+                    active_to: $('#qdr-tss-edit-active-to').val(),
+                    reference: $('#qdr-tss-edit-reference').val()
+                };
+                
+                $.post(ajaxurl, data, function(response) {
+                    if (response.success) {
+                        // Show success status
+                        $('#qdr-tss-edit-status').html('<div class="notice notice-success inline"><p>' + (response.data.message || '<?php esc_html_e('Changes saved successfully.', 'qdr-temporary-shared-storage'); ?>') + '</p></div>');
+                        
+                        // Hide modal after a delay
+                        setTimeout(function() {
+                            $('#qdr-tss-edit-modal').hide();
+                            
+                            // Reload items
+                            loadItems();
+                        }, 1000);
+                    } else {
+                        // Show error status
+                        $('#qdr-tss-edit-status').html('<div class="notice notice-error inline"><p>' + (response.data.message || '<?php esc_html_e('Error saving changes.', 'qdr-temporary-shared-storage'); ?>') + '</p></div>');
+                    }
+                }).fail(function() {
+                    // Show error status
+                    $('#qdr-tss-edit-status').html('<div class="notice notice-error inline"><p><?php esc_html_e('Error saving changes. Please try again.', 'qdr-temporary-shared-storage'); ?></p></div>');
+                });
+            });
+            
+            // Handle sorting
+            $('.qdr-tss-table th.sortable').on('click', function() {
+                var column = $(this).data('sort');
+                
+                if (currentOrderBy === column) {
+                    currentOrder = currentOrder === 'ASC' ? 'DESC' : 'ASC';
+                } else {
+                    currentOrderBy = column;
+                    currentOrder = 'ASC';
+                }
+                
+                loadItems();
+            });
+            
+            // Handle search
+            $('#qdr-tss-search-button').on('click', function() {
+                currentSearchFilename = $('#qdr-tss-search-filename').val();
+                currentSearchReference = $('#qdr-tss-search-reference').val();
+                currentPage = 1;
+                loadItems();
+            });
+            
+            // Handle search reset
+            $('#qdr-tss-reset-search').on('click', function() {
+                $('#qdr-tss-search-filename').val('');
+                $('#qdr-tss-search-reference').val('');
+                currentSearchFilename = '';
+                currentSearchReference = '';
+                currentPage = 1;
+                loadItems();
+            });
+            
+            // Handle refresh button
+            $('#qdr-tss-refresh').on('click', function() {
+                loadItems();
+            });
+            
+            // Handle modal close
+            $('.qdr-tss-modal-close, .qdr-tss-modal-cancel').on('click', function() {
+                $('.qdr-tss-modal').hide();
+            });
+            
+            // Close modal when clicking outside
+            $(window).on('click', function(e) {
+                if ($(e.target).hasClass('qdr-tss-modal')) {
+                    $('.qdr-tss-modal').hide();
+                }
+            });
+            
+            // Handle copy shortcode
+            $('.qdr-tss-copy-shortcode').on('click', function() {
+                var shortcode = $('#qdr-tss-details-shortcode code').text();
+                copyToClipboard(shortcode);
+                alert('<?php esc_html_e('Shortcode copied to clipboard.', 'qdr-temporary-shared-storage'); ?>');
+            });
+            
+            // Handle copy permalink
+            $('.qdr-tss-copy-permalink').on('click', function() {
+                var permalink = $('#qdr-tss-details-permalink a').attr('href');
+                copyToClipboard(permalink);
+                alert('<?php esc_html_e('Permalink copied to clipboard.', 'qdr-temporary-shared-storage'); ?>');
+            });
+            
+            // Handle confirm delete
+            $('.qdr-tss-confirm-delete-button').on('click', function() {
+                var id = $('#qdr-tss-delete-id').val();
+                deleteItem(id);
+            });
+            
+            // Initial load
+            loadItems();
+        });
+    </script>
+    <?php
+}

+ 173 - 0
qdr-temporary-shared-storage/admin/settings-page.php

@@ -0,0 +1,173 @@
+<?php
+/**
+ * Admin Settings Page
+ *
+ * @package QDR_Temporary_Shared_Storage
+ */
+
+// If this file is called directly, abort.
+if (!defined('WPINC')) {
+    die;
+}
+
+/**
+ * Render the Settings | Shared Storage admin page
+ */
+function qdr_tss_render_settings_page() {
+    // Get current settings
+    $settings = get_option('qdr_tss_settings', array(
+        'api_key' => '',
+        'media_category' => 'shared-storage',
+        'max_upload_size' => 1073741824, // 1GB default
+    ));
+    
+    // Get total storage usage
+    global $wpdb;
+    $table_name = $wpdb->prefix . QDR_TSS_PLUGIN_TABLE;
+    $total_size = $wpdb->get_var("SELECT SUM(file_size) FROM $table_name");
+    $total_size = $total_size ? intval($total_size) : 0;
+    ?>
+    
+    <div class="wrap qdr-tss-admin">
+        <h1><?php esc_html_e('Shared Storage Settings', 'qdr-temporary-shared-storage'); ?></h1>
+        
+        <form id="qdr-tss-settings-form" method="post">
+            <table class="form-table">
+                <tbody>
+                    <tr>
+                        <th scope="row">
+                            <label for="qdr-tss-api-key"><?php esc_html_e('API Key', 'qdr-temporary-shared-storage'); ?></label>
+                        </th>
+                        <td>
+                            <input type="text" id="qdr-tss-api-key" name="api_key" value="<?php echo esc_attr($settings['api_key']); ?>" class="regular-text">
+                            <p class="description"><?php esc_html_e('API key for REST API authentication.', 'qdr-temporary-shared-storage'); ?></p>
+                            <button type="button" id="qdr-tss-generate-key" class="button"><?php esc_html_e('Generate New Key', 'qdr-temporary-shared-storage'); ?></button>
+                        </td>
+                    </tr>
+                    
+                    <tr>
+                        <th scope="row">
+                            <label for="qdr-tss-media-category"><?php esc_html_e('Media Category', 'qdr-temporary-shared-storage'); ?></label>
+                        </th>
+                        <td>
+                            <input type="text" id="qdr-tss-media-category" name="media_category" value="<?php echo esc_attr($settings['media_category']); ?>" class="regular-text">
+                            <p class="description"><?php esc_html_e('Category to assign to uploaded media.', 'qdr-temporary-shared-storage'); ?></p>
+                        </td>
+                    </tr>
+                    
+                    <tr>
+                        <th scope="row">
+                            <label for="qdr-tss-max-upload-size"><?php esc_html_e('Maximum Storage Size (bytes)', 'qdr-temporary-shared-storage'); ?></label>
+                        </th>
+                        <td>
+                            <input type="number" id="qdr-tss-max-upload-size" name="max_upload_size" value="<?php echo esc_attr($settings['max_upload_size']); ?>" class="regular-text">
+                            <p class="description">
+                                <?php esc_html_e('Maximum total storage size in bytes. Current usage:', 'qdr-temporary-shared-storage'); ?> 
+                                <span id="qdr-tss-current-usage"><?php echo esc_html(qdr_tss_format_bytes($total_size)); ?></span>
+                                (<?php echo esc_html(qdr_tss_format_bytes($settings['max_upload_size'])); ?> <?php esc_html_e('max', 'qdr-temporary-shared-storage'); ?>)
+                            </p>
+                            <div class="qdr-tss-storage-bar">
+                                <div class="qdr-tss-storage-used" style="width: <?php echo min(100, ($total_size / $settings['max_upload_size']) * 100); ?>%"></div>
+                            </div>
+                            
+                            <p class="description">
+                                <?php esc_html_e('Common sizes:', 'qdr-temporary-shared-storage'); ?><br>
+                                1 GB = 1073741824 <?php esc_html_e('bytes', 'qdr-temporary-shared-storage'); ?><br>
+                                5 GB = 5368709120 <?php esc_html_e('bytes', 'qdr-temporary-shared-storage'); ?><br>
+                                10 GB = 10737418240 <?php esc_html_e('bytes', 'qdr-temporary-shared-storage'); ?>
+                            </p>
+                        </td>
+                    </tr>
+                </tbody>
+            </table>
+            
+            <p class="submit">
+                <button type="submit" id="qdr-tss-save-settings" class="button button-primary"><?php esc_html_e('Save Settings', 'qdr-temporary-shared-storage'); ?></button>
+                <button type="button" id="qdr-tss-purge-expired" class="button"><?php esc_html_e('Purge Expired Files', 'qdr-temporary-shared-storage'); ?></button>
+            </p>
+        </form>
+    </div>
+    
+    <script type="text/javascript">
+        jQuery(document).ready(function($) {
+            // Generate new API key
+            $('#qdr-tss-generate-key').on('click', function(e) {
+                e.preventDefault();
+                
+                // Generate a random string for API key
+                var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+                var apiKey = '';
+                for (var i = 0; i < 32; i++) {
+                    apiKey += chars.charAt(Math.floor(Math.random() * chars.length));
+                }
+                
+                $('#qdr-tss-api-key').val(apiKey);
+            });
+            
+            // Save settings
+            $('#qdr-tss-settings-form').on('submit', function(e) {
+                e.preventDefault();
+                
+                var data = {
+                    action: 'qdr_tss_save_settings',
+                    nonce: qdr_tss.nonce,
+                    api_key: $('#qdr-tss-api-key').val(),
+                    media_category: $('#qdr-tss-media-category').val(),
+                    max_upload_size: $('#qdr-tss-max-upload-size').val()
+                };
+                
+                $.post(ajaxurl, data, function(response) {
+                    if (response.success) {
+                        alert(response.data.message);
+                    } else {
+                        alert(response.data.message);
+                    }
+                });
+            });
+            
+            // Purge expired files
+            $('#qdr-tss-purge-expired').on('click', function(e) {
+                e.preventDefault();
+                
+                if (!confirm(qdr_tss.strings.confirm_purge)) {
+                    return;
+                }
+                
+                var data = {
+                    action: 'qdr_tss_purge_expired',
+                    nonce: qdr_tss.nonce
+                };
+                
+                $.post(ajaxurl, data, function(response) {
+                    if (response.success) {
+                        alert(response.data.message);
+                        // Reload page to update storage usage
+                        location.reload();
+                    } else {
+                        alert(response.data.message);
+                    }
+                });
+            });
+        });
+    </script>
+    <?php
+}
+
+/**
+ * Format bytes to human readable format
+ *
+ * @param int $bytes
+ * @param int $precision
+ * @return string
+ */
+function qdr_tss_format_bytes($bytes, $precision = 2) {
+    $units = array('B', 'KB', 'MB', 'GB', 'TB');
+    
+    $bytes = max($bytes, 0);
+    $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
+    $pow = min($pow, count($units) - 1);
+    
+    $bytes /= pow(1024, $pow);
+    
+    return round($bytes, $precision) . ' ' . $units[$pow];
+}

+ 206 - 0
qdr-temporary-shared-storage/assets/css/admin.css

@@ -0,0 +1,206 @@
+/**
+ * Admin styles for the QDR Temporary Shared Storage plugin
+ */
+
+/* Admin General */
+.qdr-tss-admin {
+    margin: 20px 0;
+}
+
+/* Toolbar */
+.qdr-tss-toolbar {
+    display: flex;
+    justify-content: space-between;
+    margin-bottom: 20px;
+}
+
+.qdr-tss-search {
+    display: flex;
+    align-items: center;
+}
+
+.qdr-tss-search input {
+    margin-right: 10px;
+    min-width: 300px;
+}
+
+/* Table */
+.qdr-tss-table-container {
+    margin-bottom: 20px;
+    overflow-x: auto;
+}
+
+.qdr-tss-table .column-id {
+    width: 80px;
+}
+
+.qdr-tss-table .column-filename {
+    width: 150px;
+}
+
+.qdr-tss-table .column-description,
+.qdr-tss-table .column-message {
+    width: 150px;
+}
+
+.qdr-tss-table .column-active-from,
+.qdr-tss-table .column-active-to,
+.qdr-tss-table .column-upload-date,
+.qdr-tss-table .column-last-download {
+    width: 120px;
+}
+
+.qdr-tss-table .column-reference {
+    width: 100px;
+}
+
+.qdr-tss-table .column-download-count {
+    width: 80px;
+    text-align: center;
+}
+
+.qdr-tss-table .column-actions {
+    width: 120px;
+    text-align: center;
+}
+
+.qdr-tss-table .sortable {
+    cursor: pointer;
+}
+
+.qdr-tss-table .sortable:hover {
+    background-color: #f0f0f0;
+}
+
+/* Pagination */
+.qdr-tss-pagination {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-top: 20px;
+}
+
+.qdr-tss-pagination-controls .button {
+    margin-left: 5px;
+}
+
+/* Modal */
+.qdr-tss-modal {
+    display: none;
+    position: fixed;
+    z-index: 9999;
+    left: 0;
+    top: 0;
+    width: 100%;
+    height: 100%;
+    overflow: auto;
+    background-color: rgba(0, 0, 0, 0.4);
+}
+
+.qdr-tss-modal-content {
+    background-color: #fefefe;
+    margin: 10% auto;
+    padding: 20px;
+    border: 1px solid #888;
+    width: 50%;
+    min-width: 500px;
+    max-width: 800px;
+    border-radius: 4px;
+    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+}
+
+.qdr-tss-modal-close {
+    color: #aaa;
+    float: right;
+    font-size: 28px;
+    font-weight: bold;
+    cursor: pointer;
+}
+
+.qdr-tss-modal-close:hover,
+.qdr-tss-modal-close:focus {
+    color: black;
+    text-decoration: none;
+    cursor: pointer;
+}
+
+/* Form */
+.qdr-tss-form-row {
+    margin: 15px 0;
+}
+
+.qdr-tss-form-row label {
+    display: block;
+    margin-bottom: 5px;
+    font-weight: bold;
+}
+
+.qdr-tss-form-row input[type="text"],
+.qdr-tss-form-row textarea {
+    width: 100%;
+}
+
+.qdr-tss-form-actions {
+    text-align: right;
+    margin-top: 20px;
+}
+
+.qdr-tss-form-actions .button {
+    margin-left: 10px;
+}
+
+/* Settings page */
+.qdr-tss-storage-bar {
+    width: 100%;
+    height: 20px;
+    background-color: #f1f1f1;
+    border-radius: 10px;
+    margin: 10px 0;
+    overflow: hidden;
+}
+
+.qdr-tss-storage-used {
+    height: 100%;
+    background-color: #2271b1;
+}
+
+/* Download page */
+.qdr-tss-download-form {
+    max-width: 600px;
+    margin: 20px auto;
+    padding: 20px;
+    background-color: #f8f8f8;
+    border-radius: 5px;
+    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+}
+
+.qdr-tss-download-info {
+    max-width: 600px;
+    margin: 20px auto;
+    padding: 20px;
+    background-color: #fff;
+    border-radius: 5px;
+    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+}
+
+.qdr-tss-error {
+    color: #d63638;
+    margin: 20px 0;
+    padding: 10px;
+    background-color: #ffdddd;
+    border-left: 4px solid #d63638;
+}
+
+.qdr-tss-button {
+    background-color: #2271b1;
+    color: #fff;
+    border: none;
+    padding: 10px 15px;
+    border-radius: 3px;
+    cursor: pointer;
+    font-size: 14px;
+}
+
+.qdr-tss-button:hover {
+    background-color: #135e96;
+}

+ 84 - 0
qdr-temporary-shared-storage/assets/js/admin.js

@@ -0,0 +1,84 @@
+/**
+ * Admin JavaScript for the QDR Temporary Shared Storage plugin
+ */
+
+(function($) {
+    'use strict';
+
+    // Initialize datepickers on dynamically created elements
+    function initDatepickers() {
+        $('.qdr-tss-datepicker').datepicker({
+            dateFormat: 'yy-mm-dd',
+            changeMonth: true,
+            changeYear: true
+        });
+    }
+
+    // Format file size to human readable format
+    function formatFileSize(bytes) {
+        if (bytes === 0) return '0 Bytes';
+        const k = 1024;
+        const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
+        const i = Math.floor(Math.log(bytes) / Math.log(k));
+        return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
+    }
+
+    // Format date to local format
+    function formatDate(dateString) {
+        if (!dateString || dateString === '0000-00-00 00:00:00') {
+            return '-';
+        }
+        
+        var date = new Date(dateString);
+        
+        // Check if date is valid
+        if (isNaN(date.getTime())) {
+            return dateString;
+        }
+        
+        return date.toLocaleString();
+    }
+
+    // Set active sort indicators
+    function updateSortIndicators(orderBy, order) {
+        // Remove all indicators
+        $('.qdr-tss-table th').removeClass('sorted asc desc');
+        
+        // Add indicator to current sort column
+        var $th = $('.qdr-tss-table th[data-sort="' + orderBy + '"]');
+        $th.addClass('sorted ' + order.toLowerCase());
+    }
+
+    // Initialize tooltips
+    function initTooltips() {
+        $('.qdr-tss-tooltip').tooltip({
+            position: {
+                my: 'center bottom-20',
+                at: 'center top',
+                using: function(position, feedback) {
+                    $(this).css(position);
+                    $('<div>')
+                        .addClass('arrow')
+                        .addClass(feedback.vertical)
+                        .addClass(feedback.horizontal)
+                        .appendTo(this);
+                }
+            }
+        });
+    }
+
+    // Document ready
+    $(function() {
+        // Initialize datepickers on page load
+        initDatepickers();
+        
+        // Initialize tooltips
+        initTooltips();
+        
+        // Add button for refreshing the list
+        $('#qdr-tss-items-list').on('updated', function() {
+            initTooltips();
+        });
+    });
+
+})(jQuery);

+ 26 - 0
qdr-temporary-shared-storage/directory-structure.txt

@@ -0,0 +1,26 @@
+qdr-temporary-shared-storage/
+│
+├── qdr-temporary-shared-storage.php          # Main plugin file
+│
+├── admin/                                    # Admin pages
+│   ├── media-page.php                        # Media management interface
+│   └── settings-page.php                     # Plugin settings page
+│
+├── assets/                                   # Frontend and admin assets
+│   ├── css/
+│   │   └── admin.css                         # Admin styles
+│   └── js/
+│       └── admin.js                          # Admin JavaScript
+│
+├── languages/                                # Translations
+│   └── qdr-temporary-shared-storage.pot      # Translation template
+│
+├── readme.txt                                # WordPress.org plugin repository readme
+└── README.md                                 # GitHub/documentation readme
+
+Note: When uploading files, the plugin creates these directories:
+
+wp-content/uploads/qdr-shared-storage/        # Main storage directory
+├── .htaccess                                 # Protects direct access to files
+├── tmp/                                      # Temporary directory for chunked uploads
+└── YYYY/MM/DD/                               # Date-based folders for file storage

+ 73 - 0
qdr-temporary-shared-storage/instructions.txt

@@ -0,0 +1,73 @@
+Write WordPress plugin called "qdr-temporary-shared-storage" in PHP, that 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)
+
+Plugin will have following features:
+
+1. Allows to upload media file (one huge) via REST API with folowing properties (use upload with chuncks):
+	original-file-name - Original file name (string)
+	description - File description (string)
+	message - Custom message (string)
+	active-from - Datetime from permalink will be active (datetime)
+	active-to - Datetime to permalink will be active (datetime) (datetime)
+	password - password for download file (string)
+	reference - Custom reference value (string)
+	
+	- returns:
+		media_id
+		permalink (or shortcode) to download file
+	
+2. Allows to edit folowing media properties by media_id via REST API:
+	message - Custom message (string)
+	active-from - Datetime from permalink will be active (datetime)
+	active-to - Datetime to permalink will be active (datetime) (datetime)
+	reference - Custom reference value (string)
+
+3. Allows to delete media by media_id via REST API
+4. Allows to get media following properties via REST API:
+	original-file-name - Original file name (string)
+	description - File description (string)
+	message - Custom message (string)
+	active-from - Datetime from permalink will be active (datetime)
+	active-to - Datetime to permalink will be active (datetime) (datetime)
+	reference - Custom reference value (string)
+5. Allows to get media collection following properties via wordpress query:
+	original-file-name - Original file name (string)
+	description - File description (string)
+	message - Custom message (string)
+	active-from - Datetime from permalink will be active (datetime)
+	active-to - Datetime to permalink will be active (datetime) (datetime)
+	reference - Custom reference value (string)
+	
+6. Allows to download media file via provided permalink and download page where must be put by user following values:
+	password
+	reference
+	
+	- count medial downloads, stores last dowload (last-download) as datetime
+
+5. Creates Cron job (one per hour) to scan for expired (active-to) media (media record and content files) and deletes them
+6. Creates in menu Media | Shared Storage page with grid that will have following columns:
+	media_id
+	original-file-name - Original file name (string)
+	description - File description (string)
+	message - Custom message (string)
+	active-from - Datetime from permalink will be active (datetime)
+	active-to - Datetime to permalink will be active (datetime) (datetime)
+	reference - Custom reference value (string)
+	upload-date
+	download-count
+	latedt-downlaod
+	
+	- allows to edit properties
+	- allows search/filter by original-file-name, reference
+	- allows to sort by all columns
+	- allows paging support
+	
+7. Creates in menu Settings | Shared Storage that shows following settings:
+	API key to use with REST client
+	Specification of Media Category to be assigned during media upload
+	Maximal upload total size - size of all media stored under category defined in field above - if limit reach upload will not be possible
+	Button to Save Settings
+	Button to Purge expired Media (Same as in cron job)
+8. All text will be translatable
+
+
+	

BIN
qdr-temporary-shared-storage/languages/qdr-temporary-shared-storage-cs_CZ.mo


+ 331 - 0
qdr-temporary-shared-storage/languages/qdr-temporary-shared-storage-cs_CZ.po

@@ -0,0 +1,331 @@
+# Copyright Quadarax (C) 2025
+# This file is distributed under the GPL-2.0+.
+msgid ""
+msgstr ""
+"Project-Id-Version: QDR Temporary Shared Storage 1.0.0\n"
+"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/qdr-temporary-"
+"shared-storage\n"
+"POT-Creation-Date: 2025-05-19T12:00:00+00:00\n"
+"PO-Revision-Date: 2025-05-19 19:13+0200\n"
+"Last-Translator: Dalibor Votruba <dvotruba@quadarax.com>\n"
+"Language-Team: \n"
+"Language: cs_CZ\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
+"X-Generator: Poedit 3.6\n"
+"X-Domain: qdr-temporary-shared-storage\n"
+
+#. Plugin Name of the plugin
+msgid "QDR Temporary Shared Storage"
+msgstr "QDR Temporary Shared Storage"
+
+#. Plugin URI of the plugin
+msgid "https://www.quadarax.com/qdr-temporary-shared-storage"
+msgstr "https://www.quadarax.com/qdr-temporary-shared-storage"
+
+#. Description of the plugin
+msgid ""
+"WordPress plugin for temporary file sharing with REST API support and "
+"password protection"
+msgstr ""
+"WordPress plugin pro dočasné sdílení souborů s podporou REST API a ochranou "
+"heslem"
+
+#. Author of the plugin
+msgid "Dalibor Votruba"
+msgstr "Dalibor Votruba"
+
+#. Author URI of the plugin
+msgid "https://www.quadarax.com"
+msgstr "https://www.quadarax.com"
+
+#: qdr-temporary-shared-storage.php:125
+msgid "Shared File Download"
+msgstr "Stažení sdíleného souboru"
+
+#: qdr-temporary-shared-storage.php:384
+msgid "Invalid API key."
+msgstr "Neplatný API klíč."
+
+#: qdr-temporary-shared-storage.php:438 qdr-temporary-shared-storage.php:485
+#: qdr-temporary-shared-storage.php:529
+msgid "Missing required parameters."
+msgstr "Chybí povinné parametry."
+
+#: qdr-temporary-shared-storage.php:443
+msgid "File exceeds maximum upload size."
+msgstr "Soubor překračuje maximální povolenou velikost."
+
+#: qdr-temporary-shared-storage.php:448
+msgid "Storage limit exceeded."
+msgstr "Překročen limit úložiště."
+
+#: qdr-temporary-shared-storage.php:494
+msgid "Invalid upload ID."
+msgstr "Neplatné ID nahrávání."
+
+#: qdr-temporary-shared-storage.php:501
+msgid "No file uploaded."
+msgstr "Žádný soubor nebyl nahrán."
+
+#: qdr-temporary-shared-storage.php:537
+msgid "Missing required field: %s"
+msgstr "Chybí povinné pole: %s"
+
+#: qdr-temporary-shared-storage.php:546
+msgid "Not all chunks have been uploaded."
+msgstr "Ne všechny části byly nahrány."
+
+#: qdr-temporary-shared-storage.php:625 qdr-temporary-shared-storage.php:655
+#: qdr-temporary-shared-storage.php:712
+msgid "Media not found."
+msgstr "Médium nenalezeno."
+
+#: qdr-temporary-shared-storage.php:690 class-qdr-tss-admin.php:157
+msgid "No changes to update."
+msgstr "Žádné změny k aktualizaci."
+
+#: qdr-temporary-shared-storage.php:732
+msgid "File not found."
+msgstr "Soubor nenalezen."
+
+#: qdr-temporary-shared-storage.php:858 qdr-temporary-shared-storage.php:954
+#: class-qdr-tss-admin.php:27 class-qdr-tss-admin.php:112
+#: class-qdr-tss-admin.php:186 class-qdr-tss-admin.php:232
+#: class-qdr-tss-admin.php:265
+msgid "Security check failed."
+msgstr "Bezpečnostní kontrola selhala."
+
+#: qdr-temporary-shared-storage.php:942
+msgid "This file is not yet available for download."
+msgstr "Tento soubor ještě není k dispozici ke stažení."
+
+#: qdr-temporary-shared-storage.php:946
+msgid "This file has expired and is no longer available for download."
+msgstr "Platnost tohoto souboru vypršela a již není k dispozici ke stažení."
+
+#: qdr-temporary-shared-storage.php:959
+msgid "Invalid password."
+msgstr "Neplatné heslo."
+
+#: qdr-temporary-shared-storage.php:965
+msgid "Invalid reference."
+msgstr "Neplatná reference."
+
+#: qdr-temporary-shared-storage.php:997
+msgid "File not found on server."
+msgstr "Soubor nebyl nalezen na serveru."
+
+#: qdr-temporary-shared-storage.php:1004
+msgid "File Information"
+msgstr "Informace o souboru"
+
+#: qdr-temporary-shared-storage.php:1005
+msgid "File Name:"
+msgstr "Název souboru:"
+
+#: qdr-temporary-shared-storage.php:1008
+msgid "Description:"
+msgstr "Popis:"
+
+#: qdr-temporary-shared-storage.php:1012
+msgid "Message:"
+msgstr "Zpráva:"
+
+#: qdr-temporary-shared-storage.php:1029 admin/media-page.php:34
+msgid "Media ID"
+msgstr "ID média"
+
+#: qdr-temporary-shared-storage.php:1035
+msgid "Password"
+msgstr "Heslo"
+
+#: qdr-temporary-shared-storage.php:1040
+msgid "Reference (if required)"
+msgstr "Reference (pokud je vyžadována)"
+
+#: qdr-temporary-shared-storage.php:1045
+msgid "Download File"
+msgstr "Stáhnout soubor"
+
+#: admin/media-page.php:16
+msgid "Shared Storage"
+msgstr "Sdílené úložiště"
+
+#: admin/media-page.php:20
+msgid "Search by filename or reference"
+msgstr "Hledání podle názvu souboru nebo reference"
+
+#: admin/media-page.php:22
+msgid "Search"
+msgstr "Vyhledat"
+
+#: admin/media-page.php:25
+msgid "Reset"
+msgstr "Reset"
+
+#: admin/media-page.php:35
+msgid "File Name"
+msgstr "Název souboru"
+
+#: admin/media-page.php:36 admin/media-page.php:67
+msgid "Description"
+msgstr "Popis"
+
+#: admin/media-page.php:37 admin/media-page.php:72
+msgid "Message"
+msgstr "Zpráva"
+
+#: admin/media-page.php:38 admin/media-page.php:77
+msgid "Active From"
+msgstr "Aktivní od"
+
+#: admin/media-page.php:39 admin/media-page.php:82
+msgid "Active To"
+msgstr "Aktivní do"
+
+#: admin/media-page.php:40 admin/media-page.php:87
+msgid "Reference"
+msgstr "Reference"
+
+#: admin/media-page.php:41
+msgid "Upload Date"
+msgstr "Datum nahrání"
+
+#: admin/media-page.php:42
+msgid "Downloads"
+msgstr "Stažení"
+
+#: admin/media-page.php:43
+msgid "Last Download"
+msgstr "Poslední stažení"
+
+#: admin/media-page.php:44
+msgid "Actions"
+msgstr "Akce"
+
+#: admin/media-page.php:49
+msgid "Loading..."
+msgstr "Načítání..."
+
+#: admin/media-page.php:61
+msgid "Edit Shared File"
+msgstr "Upravit sdílený soubor"
+
+#: admin/media-page.php:92
+msgid "Save Changes"
+msgstr "Uložit změny"
+
+#: admin/media-page.php:93
+msgid "Cancel"
+msgstr "Zrušit"
+
+#: admin/media-page.php:112
+msgid "No items found."
+msgstr "Nebyly nalezeny žádné položky."
+
+#: admin/media-page.php:138
+msgid "Edit"
+msgstr "Upravit"
+
+#: admin/media-page.php:139
+msgid "Delete"
+msgstr "Smazat"
+
+#: admin/media-page.php:168
+msgid "Showing"
+msgstr "Zobrazeno"
+
+#: admin/media-page.php:168
+msgid "of"
+msgstr "z"
+
+#: admin/settings-page.php:31
+msgid "Shared Storage Settings"
+msgstr "Nastavení sdíleného úložiště"
+
+#: admin/settings-page.php:39
+msgid "API Key"
+msgstr "API klíč"
+
+#: admin/settings-page.php:42
+msgid "API key for REST API authentication."
+msgstr "API klíč pro REST API autentizaci."
+
+#: admin/settings-page.php:43
+msgid "Generate New Key"
+msgstr "Vygenerovat nový klíč"
+
+#: admin/settings-page.php:49
+msgid "Media Category"
+msgstr "Kategorie médií"
+
+#: admin/settings-page.php:52
+msgid "Category to assign to uploaded media."
+msgstr "Kategorie přiřazená nahraným médiím."
+
+#: admin/settings-page.php:58
+msgid "Maximum Storage Size (bytes)"
+msgstr "Maximální velikost úložiště (bajtů)"
+
+#: admin/settings-page.php:61
+msgid "Maximum total storage size in bytes. Current usage:"
+msgstr "Maximální celková velikost úložiště v bajtech. Aktuální využití:"
+
+#: admin/settings-page.php:63
+msgid "max"
+msgstr "maximum"
+
+#: admin/settings-page.php:70
+msgid "Common sizes:"
+msgstr "Běžné velikosti:"
+
+#: admin/settings-page.php:71 admin/settings-page.php:72
+#: admin/settings-page.php:73
+msgid "bytes"
+msgstr "bajtů"
+
+#: admin/settings-page.php:79
+msgid "Save Settings"
+msgstr "Uložit nastavení"
+
+#: admin/settings-page.php:80
+msgid "Purge Expired Files"
+msgstr "Vymazat expirované soubory"
+
+#: class-qdr-tss-admin.php:117 class-qdr-tss-admin.php:191
+msgid "Missing item ID."
+msgstr "Chybí ID položky."
+
+#: class-qdr-tss-admin.php:167
+msgid "Failed to update item."
+msgstr "Nepodařilo se aktualizovat položku."
+
+#: class-qdr-tss-admin.php:174
+msgid "Item updated successfully."
+msgstr "Položka byla úspěšně aktualizována."
+
+#: class-qdr-tss-admin.php:204
+msgid "Item not found."
+msgstr "Položka nenalezena."
+
+#: class-qdr-tss-admin.php:217
+msgid "Failed to delete item."
+msgstr "Nepodařilo se smazat položku."
+
+#: class-qdr-tss-admin.php:221
+msgid "Item deleted successfully."
+msgstr "Položka byla úspěšně smazána."
+
+#: class-qdr-tss-admin.php:254
+msgid "Settings saved successfully."
+msgstr "Nastavení bylo úspěšně uloženo."
+
+#: class-qdr-tss-admin.php:277
+msgid "%d expired file has been purged."
+msgid_plural "%d expired files have been purged."
+msgstr[0] "%d expirovaný soubor byl vymazán."
+msgstr[1] "%d expirované soubory byly vymazány."
+msgstr[2] "%d expirovaných souborů bylo vymazáno."

+ 327 - 0
qdr-temporary-shared-storage/languages/qdr-temporary-shared-storage.pot

@@ -0,0 +1,327 @@
+# Copyright Quadarax (C) 2025
+# This file is distributed under the GPL-2.0+.
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: QDR Temporary Shared Storage 1.0.0\n"
+"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/qdr-temporary-"
+"shared-storage\n"
+"POT-Creation-Date: 2025-05-19 19:12+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 3.6\n"
+"X-Domain: qdr-temporary-shared-storage\n"
+
+#. Plugin Name of the plugin
+msgid "QDR Temporary Shared Storage"
+msgstr ""
+
+#. Plugin URI of the plugin
+msgid "https://www.quadarax.com/qdr-temporary-shared-storage"
+msgstr ""
+
+#. Description of the plugin
+msgid ""
+"WordPress plugin for temporary file sharing with REST API support and "
+"password protection"
+msgstr ""
+
+#. Author of the plugin
+msgid "Dalibor Votruba"
+msgstr ""
+
+#. Author URI of the plugin
+msgid "https://www.quadarax.com"
+msgstr ""
+
+#: qdr-temporary-shared-storage.php:125
+msgid "Shared File Download"
+msgstr ""
+
+#: qdr-temporary-shared-storage.php:384
+msgid "Invalid API key."
+msgstr ""
+
+#: qdr-temporary-shared-storage.php:438 qdr-temporary-shared-storage.php:485
+#: qdr-temporary-shared-storage.php:529
+msgid "Missing required parameters."
+msgstr ""
+
+#: qdr-temporary-shared-storage.php:443
+msgid "File exceeds maximum upload size."
+msgstr ""
+
+#: qdr-temporary-shared-storage.php:448
+msgid "Storage limit exceeded."
+msgstr ""
+
+#: qdr-temporary-shared-storage.php:494
+msgid "Invalid upload ID."
+msgstr ""
+
+#: qdr-temporary-shared-storage.php:501
+msgid "No file uploaded."
+msgstr ""
+
+#: qdr-temporary-shared-storage.php:537
+msgid "Missing required field: %s"
+msgstr ""
+
+#: qdr-temporary-shared-storage.php:546
+msgid "Not all chunks have been uploaded."
+msgstr ""
+
+#: qdr-temporary-shared-storage.php:625 qdr-temporary-shared-storage.php:655
+#: qdr-temporary-shared-storage.php:712
+msgid "Media not found."
+msgstr ""
+
+#: qdr-temporary-shared-storage.php:690 class-qdr-tss-admin.php:157
+msgid "No changes to update."
+msgstr ""
+
+#: qdr-temporary-shared-storage.php:732
+msgid "File not found."
+msgstr ""
+
+#: qdr-temporary-shared-storage.php:858 qdr-temporary-shared-storage.php:954
+#: class-qdr-tss-admin.php:27 class-qdr-tss-admin.php:112
+#: class-qdr-tss-admin.php:186 class-qdr-tss-admin.php:232
+#: class-qdr-tss-admin.php:265
+msgid "Security check failed."
+msgstr ""
+
+#: qdr-temporary-shared-storage.php:942
+msgid "This file is not yet available for download."
+msgstr ""
+
+#: qdr-temporary-shared-storage.php:946
+msgid "This file has expired and is no longer available for download."
+msgstr ""
+
+#: qdr-temporary-shared-storage.php:959
+msgid "Invalid password."
+msgstr ""
+
+#: qdr-temporary-shared-storage.php:965
+msgid "Invalid reference."
+msgstr ""
+
+#: qdr-temporary-shared-storage.php:997
+msgid "File not found on server."
+msgstr ""
+
+#: qdr-temporary-shared-storage.php:1004
+msgid "File Information"
+msgstr ""
+
+#: qdr-temporary-shared-storage.php:1005
+msgid "File Name:"
+msgstr ""
+
+#: qdr-temporary-shared-storage.php:1008
+msgid "Description:"
+msgstr ""
+
+#: qdr-temporary-shared-storage.php:1012
+msgid "Message:"
+msgstr ""
+
+#: qdr-temporary-shared-storage.php:1029 admin/media-page.php:34
+msgid "Media ID"
+msgstr ""
+
+#: qdr-temporary-shared-storage.php:1035
+msgid "Password"
+msgstr ""
+
+#: qdr-temporary-shared-storage.php:1040
+msgid "Reference (if required)"
+msgstr ""
+
+#: qdr-temporary-shared-storage.php:1045
+msgid "Download File"
+msgstr ""
+
+#: admin/media-page.php:16
+msgid "Shared Storage"
+msgstr ""
+
+#: admin/media-page.php:20
+msgid "Search by filename or reference"
+msgstr ""
+
+#: admin/media-page.php:22
+msgid "Search"
+msgstr ""
+
+#: admin/media-page.php:25
+msgid "Reset"
+msgstr ""
+
+#: admin/media-page.php:35
+msgid "File Name"
+msgstr ""
+
+#: admin/media-page.php:36 admin/media-page.php:67
+msgid "Description"
+msgstr ""
+
+#: admin/media-page.php:37 admin/media-page.php:72
+msgid "Message"
+msgstr ""
+
+#: admin/media-page.php:38 admin/media-page.php:77
+msgid "Active From"
+msgstr ""
+
+#: admin/media-page.php:39 admin/media-page.php:82
+msgid "Active To"
+msgstr ""
+
+#: admin/media-page.php:40 admin/media-page.php:87
+msgid "Reference"
+msgstr ""
+
+#: admin/media-page.php:41
+msgid "Upload Date"
+msgstr ""
+
+#: admin/media-page.php:42
+msgid "Downloads"
+msgstr ""
+
+#: admin/media-page.php:43
+msgid "Last Download"
+msgstr ""
+
+#: admin/media-page.php:44
+msgid "Actions"
+msgstr ""
+
+#: admin/media-page.php:49
+msgid "Loading..."
+msgstr ""
+
+#: admin/media-page.php:61
+msgid "Edit Shared File"
+msgstr ""
+
+#: admin/media-page.php:92
+msgid "Save Changes"
+msgstr ""
+
+#: admin/media-page.php:93
+msgid "Cancel"
+msgstr ""
+
+#: admin/media-page.php:112
+msgid "No items found."
+msgstr ""
+
+#: admin/media-page.php:138
+msgid "Edit"
+msgstr ""
+
+#: admin/media-page.php:139
+msgid "Delete"
+msgstr ""
+
+#: admin/media-page.php:168
+msgid "Showing"
+msgstr ""
+
+#: admin/media-page.php:168
+msgid "of"
+msgstr ""
+
+#: admin/settings-page.php:31
+msgid "Shared Storage Settings"
+msgstr ""
+
+#: admin/settings-page.php:39
+msgid "API Key"
+msgstr ""
+
+#: admin/settings-page.php:42
+msgid "API key for REST API authentication."
+msgstr ""
+
+#: admin/settings-page.php:43
+msgid "Generate New Key"
+msgstr ""
+
+#: admin/settings-page.php:49
+msgid "Media Category"
+msgstr ""
+
+#: admin/settings-page.php:52
+msgid "Category to assign to uploaded media."
+msgstr ""
+
+#: admin/settings-page.php:58
+msgid "Maximum Storage Size (bytes)"
+msgstr ""
+
+#: admin/settings-page.php:61
+msgid "Maximum total storage size in bytes. Current usage:"
+msgstr ""
+
+#: admin/settings-page.php:63
+msgid "max"
+msgstr ""
+
+#: admin/settings-page.php:70
+msgid "Common sizes:"
+msgstr ""
+
+#: admin/settings-page.php:71 admin/settings-page.php:72
+#: admin/settings-page.php:73
+msgid "bytes"
+msgstr ""
+
+#: admin/settings-page.php:79
+msgid "Save Settings"
+msgstr ""
+
+#: admin/settings-page.php:80
+msgid "Purge Expired Files"
+msgstr ""
+
+#: class-qdr-tss-admin.php:117 class-qdr-tss-admin.php:191
+msgid "Missing item ID."
+msgstr ""
+
+#: class-qdr-tss-admin.php:167
+msgid "Failed to update item."
+msgstr ""
+
+#: class-qdr-tss-admin.php:174
+msgid "Item updated successfully."
+msgstr ""
+
+#: class-qdr-tss-admin.php:204
+msgid "Item not found."
+msgstr ""
+
+#: class-qdr-tss-admin.php:217
+msgid "Failed to delete item."
+msgstr ""
+
+#: class-qdr-tss-admin.php:221
+msgid "Item deleted successfully."
+msgstr ""
+
+#: class-qdr-tss-admin.php:254
+msgid "Settings saved successfully."
+msgstr ""
+
+#: class-qdr-tss-admin.php:277
+msgid "%d expired file has been purged."
+msgid_plural "%d expired files have been purged."
+msgstr[0] ""
+msgstr[1] ""

+ 1235 - 0
qdr-temporary-shared-storage/qdr-temporary-shared-storage.php

@@ -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';
+        

+ 239 - 0
qdr-temporary-shared-storage/readme.md

@@ -0,0 +1,239 @@
+# QDR - Temporary Shared Storage (via REST API)
+
+**Version: 1.0.0**
+
+A WordPress plugin that extends functionality for REST API interface to upload temporary media files and provide permalink to password-protected download pages.
+
+## Description
+
+QDR Temporary Shared Storage enables secure sharing of large files through WordPress with complete control over access and expiration. This plugin is ideal for businesses that need to share sensitive documents, large media files, or private content with clients or team members for a limited time.
+
+## Requirements
+
+- WordPress 6.8.1 or higher
+- PHP 8.2 or higher
+- WooCommerce 9.0 or higher (if WooCommerce integration is used)
+
+## Features
+
+- **REST API Integration**: Upload and manage files programmatically
+- **Chunked File Uploads**: Support for uploading large files in chunks
+- **Password Protection**: Secure access to shared files
+- **Automatic Expiration**: Files automatically expire and are removed after their expiration date
+- **Download Tracking**: Track number of downloads and last download time
+- **Admin Interface**: Manage shared files through a user-friendly admin interface
+- **Custom Reference Codes**: Add reference codes for additional verification
+- **Storage Management**: Set limits on total storage used by shared files
+- **WordPress Media Library Integration**: Files are properly registered in WordPress media system
+
+## Installation
+
+1. Upload the `qdr-temporary-shared-storage` directory to the `/wp-content/plugins/` directory
+2. Activate the plugin through the 'Plugins' menu in WordPress
+3. Configure the plugin settings in 'Settings > Shared Storage'
+
+## Usage
+
+### Admin Interface
+
+- **Media > Shared Storage**: Manage all shared files
+- **Settings > Shared Storage**: Configure plugin settings
+
+### REST API Endpoints
+
+All REST API requests require an API key sent in the `X-Api-Key` header.
+
+#### Upload Files (Chunked)
+
+1. **Initialize upload**:
+```
+POST /wp-json/qdr-tss/v1/upload/init
+```
+Parameters:
+- `original_file_name`: Original file name
+- `file_size`: File size in bytes
+
+2. **Upload chunks**:
+```
+POST /wp-json/qdr-tss/v1/upload/chunk
+```
+Parameters:
+- `upload_id`: Upload ID from initialization
+- `chunk_index`: Index of the current chunk
+- `total_chunks`: Total number of chunks
+- `chunk`: File data (multipart/form-data)
+
+3. **Finalize upload**:
+```
+POST /wp-json/qdr-tss/v1/upload/finalize
+```
+Parameters:
+- `upload_id`: Upload ID from initialization
+- `total_chunks`: Total number of chunks
+- `original_file_name`: Original file name
+- `description`: File description
+- `message`: Custom message (optional)
+- `active_from`: Date from which permalink will be active (optional)
+- `active_to`: Date until which permalink will be active (optional)
+- `password`: Password for download protection
+- `reference`: Custom reference value (optional)
+
+#### Manage Files
+
+- **Get file details**:
+```
+GET /wp-json/qdr-tss/v1/media/{id}
+```
+
+- **Update file properties**:
+```
+PUT /wp-json/qdr-tss/v1/media/{id}
+```
+Parameters:
+- `message`: Custom message (optional)
+- `active_from`: Date from which permalink will be active (optional)
+- `active_to`: Date until which permalink will be active (optional)
+- `reference`: Custom reference value (optional)
+- `description`: File description (optional)
+
+- **Delete file**:
+```
+DELETE /wp-json/qdr-tss/v1/media/{id}
+```
+
+- **Get collection of files**:
+```
+GET /wp-json/qdr-tss/v1/media
+```
+Parameters:
+- `page`: Page number (optional, default: 1)
+- `per_page`: Items per page (optional, default: 10)
+- `orderby`: Field to sort by (optional)
+- `order`: Sort order, ASC or DESC (optional)
+- `original_file_name`: Filter by file name (optional)
+- `reference`: Filter by reference (optional)
+
+### Shortcode
+
+To display a download form for a specific file:
+
+```
+[qdr_tss_download id="123"]
+```
+
+To display a generic download form where users can enter an ID, password, and reference:
+
+```
+[qdr_tss_download]
+```
+
+## Configuration Options
+
+### API Key
+Generate or specify an API key for REST API authentication.
+
+### Media Category
+Specify a category to assign to uploaded media files.
+
+### Maximum Storage Size
+Set the maximum total storage size for all shared files.
+
+## Example API Usage (PHP)
+
+```php
+// Initialize upload
+$ch = curl_init('https://www.quadarax.com/wp-json/qdr-tss/v1/upload/init');
+curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+curl_setopt($ch, CURLOPT_POST, true);
+curl_setopt($ch, CURLOPT_HTTPHEADER, [
+    'X-Api-Key: your-api-key-here'
+]);
+curl_setopt($ch, CURLOPT_POSTFIELDS, [
+    'original_file_name' => 'example.pdf',
+    'file_size' => filesize('path/to/example.pdf')
+]);
+$response = json_decode(curl_exec($ch));
+curl_close($ch);
+
+$upload_id = $response->upload_id;
+
+// Upload chunks
+$file = fopen('path/to/example.pdf', 'rb');
+$chunk_size = 1024 * 1024; // 1MB chunks
+$total_size = filesize('path/to/example.pdf');
+$total_chunks = ceil($total_size / $chunk_size);
+
+for ($i = 0; $i < $total_chunks; $i++) {
+    $chunk_data = fread($file, $chunk_size);
+    
+    $ch = curl_init('https://www.quadarax.com/wp-json/qdr-tss/v1/upload/chunk');
+    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+    curl_setopt($ch, CURLOPT_POST, true);
+    curl_setopt($ch, CURLOPT_HTTPHEADER, [
+        'X-Api-Key: your-api-key-here'
+    ]);
+    
+    $post_data = [
+        'upload_id' => $upload_id,
+        'chunk_index' => $i,
+        'total_chunks' => $total_chunks,
+        'chunk' => new CURLFile('php://temp', 'application/octet-stream', 'chunk')
+    ];
+    
+    // Write chunk data to temp file
+    $temp = fopen('php://temp', 'wb+');
+    fwrite($temp, $chunk_data);
+    rewind($temp);
+    
+    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
+    $response = json_decode(curl_exec($ch));
+    curl_close($ch);
+    
+    fclose($temp);
+}
+
+fclose($file);
+
+// Finalize upload
+$ch = curl_init('https://www.quadarax.com/wp-json/qdr-tss/v1/upload/finalize');
+curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+curl_setopt($ch, CURLOPT_POST, true);
+curl_setopt($ch, CURLOPT_HTTPHEADER, [
+    'X-Api-Key: your-api-key-here'
+]);
+curl_setopt($ch, CURLOPT_POSTFIELDS, [
+    'upload_id' => $upload_id,
+    'total_chunks' => $total_chunks,
+    'original_file_name' => 'example.pdf',
+    'description' => 'Example PDF file',
+    'message' => 'Please review this document',
+    'password' => 'secure_password',
+    'reference' => 'REF123',
+    'active_to' => date('Y-m-d H:i:s', strtotime('+30 days'))
+]);
+$response = json_decode(curl_exec($ch));
+curl_close($ch);
+
+// Get download link and media ID
+$media_id = $response->media_id;
+$permalink = $response->permalink;
+```
+
+## Security Considerations
+
+- All shared files are password-protected
+- Files are stored in a protected directory that cannot be accessed directly
+- Automatic expiration prevents unauthorized access to outdated files
+- API authentication through an API key ensures only authorized applications can upload files
+
+## License
+
+GPL v2 or later - https://www.gnu.org/licenses/gpl-2.0.html
+
+## Author
+
+[Dalibor Votruba](https://www.quadarax.com)
+
+## Support
+
+For support or feature requests, please visit [QDR Plugin Page](https://www.quadarax.com/plugins/qdr-temporary-shared-storage).

+ 85 - 0
qdr-temporary-shared-storage/readme.txt

@@ -0,0 +1,85 @@
+=== QDR Temporary Shared Storage ===
+Contributors: qdr
+Tags: file sharing, media, rest api, temporary storage, password protection
+Requires at least: 5.0
+Tested up to: 6.4
+Requires PHP: 7.2
+Stable tag: 1.0.0
+License: GPLv2 or later
+License URI: http://www.gnu.org/licenses/gpl-2.0.html
+
+WordPress plugin for temporary file sharing with REST API support and password protection.
+
+== Description ==
+
+QDR Temporary Shared Storage extends WordPress functionality by providing a REST API interface to upload temporary media files and share them via password-protected download links.
+
+= Key Features =
+
+* Upload large media files via REST API with chunked uploads
+* Password protection for all shared files
+* Automatic expiration of shared files
+* Download count tracking
+* Admin interface for managing shared files
+* Configurable storage limits
+* REST API for integration with external applications
+
+= REST API Endpoints =
+
+* `POST /wp-json/qdr-tss/v1/upload/init` - Initialize chunked upload
+* `POST /wp-json/qdr-tss/v1/upload/chunk` - Upload a file chunk
+* `POST /wp-json/qdr-tss/v1/upload/finalize` - Finalize upload and create shared file
+* `GET /wp-json/qdr-tss/v1/media/{id}` - Get shared file details
+* `PUT /wp-json/qdr-tss/v1/media/{id}` - Update shared file properties
+* `DELETE /wp-json/qdr-tss/v1/media/{id}` - Delete shared file
+* `GET /wp-json/qdr-tss/v1/media` - Get collection of shared files
+
+= Translations =
+
+* English
+
+== Installation ==
+
+1. Upload the plugin files to the `/wp-content/plugins/qdr-temporary-shared-storage` directory, or install the plugin through the WordPress plugins screen directly.
+2. Activate the plugin through the 'Plugins' screen in WordPress.
+3. Configure the plugin settings in 'Settings > Shared Storage'.
+
+== Frequently Asked Questions ==
+
+= How do I use the REST API? =
+
+You need to include the API key in your requests as an HTTP header:
+
+```
+X-Api-Key: your-api-key-here
+```
+
+See the plugin settings page to view or generate your API key.
+
+= How large can uploaded files be? =
+
+The plugin supports files of any size through chunked uploads. The maximum total storage size can be configured in the settings.
+
+= How long are files kept? =
+
+Each file has configurable 'Active From' and 'Active To' dates. Files are automatically deleted after their expiration date. By default, files expire 30 days after upload.
+
+= Can I manually delete expired files? =
+
+Yes, you can manually purge expired files from the Settings > Shared Storage page.
+
+== Screenshots ==
+
+1. Admin interface for managing shared files
+2. Settings page
+3. Download page
+
+== Changelog ==
+
+= 1.0.0 =
+* Initial release
+
+== Upgrade Notice ==
+
+= 1.0.0 =
+Initial release