|
|
1 anno fa | |
|---|---|---|
| .. | ||
| @client | 1 anno fa | |
| @documentation | 1 anno fa | |
| admin | 1 anno fa | |
| includes | 1 anno fa | |
| languages | 1 anno fa | |
| public | 1 anno fa | |
| rest-api | 1 anno fa | |
| instructions.txt | 1 anno fa | |
| pack.cmd | 1 anno fa | |
| qdr-temporary-shared-storage.php | 1 anno fa | |
| readme.md | 1 anno fa | |
| uninstall.php | 1 anno fa | |
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.
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.
qdr-temporary-shared-storage directory to the /wp-content/plugins/ directoryTo 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]
The plugin provides a complete REST API for programmatically uploading, managing, and downloading shared files.
All API requests require an API key sent in the X-Api-Key HTTP header. You can generate or view your API key in the plugin settings page at "Settings > Shared Storage".
// Initialize chunked upload
$ch = curl_init('https://www.example.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);
// Upload the chunk
// ...
}
// Finalize upload
$ch = curl_init('https://www.example.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;
All API requests require an API key sent in the X-Api-Key HTTP header. You can generate or view your API key in the plugin settings page at "Settings > Shared Storage".
The base URL for all API endpoints is:
https://your-wordpress-site.com/wp-json/qdr-tss/v1/
Prepares the server for a chunked upload.
/upload/initPOSTParameters:
original_file_name: The original name of the file being uploadedfile_size: The total size of the file in bytesResponse:
{
"upload_id": "5f8d7a6b9c3e",
"status": "initialized"
}
Uploads a single chunk of the file.
/upload/chunkPOSTParameters:
upload_id: The upload ID received from the initializationchunk_index: The index of the current chunk (starting from 0)total_chunks: The total number of chunkschunk: The chunk file data (multipart/form-data)Response:
{
"upload_id": "5f8d7a6b9c3e",
"chunk_index": 0,
"status": "chunk_uploaded"
}
Combines all chunks and creates the file record.
/upload/finalizePOSTParameters:
upload_id: The upload ID received from the initializationtotal_chunks: The total number of chunksoriginal_file_name: The original name of the filedescription: Description of the filemessage: Custom message (optional)active_from: Date from which permalink will be active (optional, defaults to current time)active_to: Date until which permalink will be active (optional, defaults to 30 days from now)password: Password for download protectionreference: Custom reference value (optional)Response:
{
"media_id": "1621234567",
"permalink": "https://your-wordpress-site.com/shared-file-download/1621234567",
"shortcode": "[qdr_tss_download id=\"1621234567\"]"
}
Retrieves details about a specific file.
/media/{media_id}Method: GET
Response:
{
"id": 1,
"media_id": "1621234567",
"original_file_name": "example.pdf",
"description": "Example document",
"message": "Please review this document",
"active_from": "2025-05-19 15:30:00",
"active_to": "2025-06-18 15:30:00",
"reference": "REF123",
"upload_date": "2025-05-19 15:30:00",
"download_count": 5,
"last_download": "2025-05-20 10:45:30",
"file_size": 1048576
}
Updates properties of a specific file.
/media/{media_id}PUTParameters:
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)Response: Updated file details (same format as Get File Details)
Deletes a specific file.
/media/{media_id}Method: DELETE
Response:
{
"deleted": true,
"media_id": "1621234567"
}
Retrieves a list of files with pagination and filtering.
/mediaGETParameters:
page: Page number (optional, default: 1)per_page: Items per page (optional, default: 10)orderby: Field to sort by (optional, default: "upload_date")order: Sort order (optional, default: "DESC")original_file_name: Filter by file name (optional)reference: Filter by reference (optional)Response:
[
{
"id": 1,
"media_id": "1621234567",
"original_file_name": "example.pdf",
"description": "Example document",
"message": "Please review this document",
"active_from": "2025-05-19 15:30:00",
"active_to": "2025-06-18 15:30:00",
"reference": "REF123",
"upload_date": "2025-05-19 15:30:00",
"download_count": 5,
"last_download": "2025-05-20 10:45:30",
"file_size": 1048576
},
...
]
<?php
/**
* Example of uploading a file using chunked upload
*/
function uploadSharedFile($file_path, $description, $password, $reference = '') {
$api_key = 'your-api-key-here';
$api_url = 'https://your-wordpress-site.com/wp-json/qdr-tss/v1';
// File information
$file_name = basename($file_path);
$file_size = filesize($file_path);
$chunk_size = 1024 * 1024; // 1MB chunks
$total_chunks = ceil($file_size / $chunk_size);
// Step 1: Initialize upload
$ch = curl_init($api_url . '/upload/init');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-Api-Key: ' . $api_key,
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'original_file_name' => $file_name,
'file_size' => $file_size
]));
$response = curl_exec($ch);
curl_close($ch);
$init_result = json_decode($response, true);
if (!isset($init_result['upload_id'])) {
throw new Exception('Failed to initialize upload: ' . $response);
}
$upload_id = $init_result['upload_id'];
// Step 2: Upload chunks
$file = fopen($file_path, 'rb');
for ($i = 0; $i < $total_chunks; $i++) {
// Create a temporary file for the chunk
$chunk_file = tempnam(sys_get_temp_dir(), 'chunk');
$chunk_handle = fopen($chunk_file, 'wb');
// Read chunk from original file
$chunk_data = fread($file, $chunk_size);
fwrite($chunk_handle, $chunk_data);
fclose($chunk_handle);
// Upload the chunk
$ch = curl_init($api_url . '/upload/chunk');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-Api-Key: ' . $api_key
]);
$post_data = [
'upload_id' => $upload_id,
'chunk_index' => $i,
'total_chunks' => $total_chunks,
'chunk' => new CURLFile($chunk_file, 'application/octet-stream', 'chunk')
];
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
$response = curl_exec($ch);
curl_close($ch);
// Delete temporary chunk file
unlink($chunk_file);
$chunk_result = json_decode($response, true);
if (!isset($chunk_result['status']) || $chunk_result['status'] !== 'chunk_uploaded') {
throw new Exception('Failed to upload chunk ' . $i . ': ' . $response);
}
// Display progress
echo "Uploaded chunk " . ($i + 1) . " of " . $total_chunks . "\n";
}
fclose($file);
// Step 3: Finalize upload
$ch = curl_init($api_url . '/upload/finalize');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-Api-Key: ' . $api_key,
'Content-Type: application/json'
]);
// Set expiration to 30 days from now
$active_to = date('Y-m-d H:i:s', strtotime('+30 days'));
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'upload_id' => $upload_id,
'total_chunks' => $total_chunks,
'original_file_name' => $file_name,
'description' => $description,
'message' => 'Uploaded via API example',
'password' => $password,
'reference' => $reference,
'active_to' => $active_to
]));
$response = curl_exec($ch);
curl_close($ch);
$final_result = json_decode($response, true);
if (!isset($final_result['media_id'])) {
throw new Exception('Failed to finalize upload: ' . $response);
}
return $final_result;
}
/**
* Uploads a file using chunked upload
* @param {File} file The file to upload
* @param {string} description File description
* @param {string} password Password for download protection
* @param {string} reference Optional reference
* @param {function} progressCallback Callback for upload progress
* @returns {Promise} Promise resolving to the upload result
*/
async function uploadSharedFile(file, description, password, reference = '', progressCallback = null) {
const apiKey = 'your-api-key-here';
const apiUrl = 'https://your-wordpress-site.com/wp-json/qdr-tss/v1';
const chunkSize = 1024 * 1024; // 1MB chunks
const totalChunks = Math.ceil(file.size / chunkSize);
// Step 1: Initialize upload
const initResponse = await fetch(`${apiUrl}/upload/init`, {
method: 'POST',
headers: {
'X-Api-Key': apiKey,
'Content-Type': 'application/json'
},
body: JSON.stringify({
original_file_name: file.name,
file_size: file.size
})
});
const initResult = await initResponse.json();
if (!initResult.upload_id) {
throw new Error('Failed to initialize upload');
}
const uploadId = initResult.upload_id;
// Step 2: Upload chunks
for (let i = 0; i < totalChunks; i++) {
const start = i * chunkSize;
const end = Math.min(file.size, start + chunkSize);
const chunk = file.slice(start, end);
const formData = new FormData();
formData.append('upload_id', uploadId);
formData.append('chunk_index', i);
formData.append('total_chunks', totalChunks);
formData.append('chunk', chunk);
const chunkResponse = await fetch(`${apiUrl}/upload/chunk`, {
method: 'POST',
headers: {
'X-Api-Key': apiKey
},
body: formData
});
const chunkResult = await chunkResponse.json();
if (!chunkResult.status || chunkResult.status !== 'chunk_uploaded') {
throw new Error(`Failed to upload chunk ${i}`);
}
// Update progress
if (progressCallback) {
progressCallback((i + 1) / totalChunks * 100);
}
}
// Step 3: Finalize upload
const activeToDate = new Date();
activeToDate.setDate(activeToDate.getDate() + 30); // 30 days from now
const activeTo = activeToDate.toISOString().slice(0, 19).replace('T', ' ');
const finalizeResponse = await fetch(`${apiUrl}/upload/finalize`, {
method: 'POST',
headers: {
'X-Api-Key': apiKey,
'Content-Type': 'application/json'
},
body: JSON.stringify({
upload_id: uploadId,
total_chunks: totalChunks,
original_file_name: file.name,
description: description,
message: 'Uploaded via JavaScript API example',
password: password,
reference: reference,
active_to: activeTo
})
});
const finalResult = await finalizeResponse.json();
if (!finalResult.media_id) {
throw new Error('Failed to finalize upload');
}
return finalResult;
}
All API endpoints return standard HTTP status codes:
200 OK: Request succeeded201 Created: Resource created successfully400 Bad Request: Invalid request parameters401 Unauthorized: Missing API key403 Forbidden: Invalid API key404 Not Found: Resource not found500 Internal Server Error: Server errorError responses include a message explaining the error:
{
"code": "qdr_tss_not_found",
"message": "Media not found.",
"data": {
"status": 404
}
}
Generate or specify an API key for REST API authentication.
Specify a category to assign to uploaded media files.
Set the maximum total storage size for all shared files.
GPL v2 or later - https://www.gnu.org/licenses/gpl-2.0.html