This guide provides instructions and examples for integrating the QDR Temporary Shared Storage plugin with various applications and systems.
You can create a simple HTML form that interacts with the plugin's REST API.
<!DOCTYPE html>
<html>
<head>
<title>File Upload</title>
<style>
.progress {
width: 100%;
height: 20px;
background-color: #f3f3f3;
margin-top: 10px;
}
.progress-bar {
height: 100%;
background-color: #4CAF50;
width: 0%;
transition: width 0.3s;
}
</style>
</head>
<body>
<h1>Upload a File</h1>
<form id="upload-form">
<div>
<label for="file">File:</label>
<input type="file" id="file" required>
</div>
<div>
<label for="description">Description:</label>
<input type="text" id="description" required>
</div>
<div>
<label for="message">Message:</label>
<textarea id="message"></textarea>
</div>
<div>
<label for="password">Password:</label>
<input type="password" id="password" required>
</div>
<div>
<label for="reference">Reference:</label>
<input type="text" id="reference">
</div>
<div>
<label for="active-days">Active for (days):</label>
<input type="number" id="active-days" value="30" min="1" max="365">
</div>
<button type="submit">Upload</button>
</form>
<div class="progress">
<div class="progress-bar" id="progress-bar"></div>
</div>
<div id="result"></div>
<script>
document.getElementById('upload-form').addEventListener('submit', async function(e) {
e.preventDefault();
const apiKey = 'your-api-key-here';
const apiUrl = 'https://your-wordpress-site.com/wp-json/qdr-tss/v1';
const file = document.getElementById('file').files[0];
if (!file) {
alert('Please select a file');
return;
}
const description = document.getElementById('description').value;
const message = document.getElementById('message').value;
const password = document.getElementById('password').value;
const reference = document.getElementById('reference').value;
const activeDays = document.getElementById('active-days').value;
const progressBar = document.getElementById('progress-bar');
const resultDiv = document.getElementById('result');
try {
// Call the uploadSharedFile function
resultDiv.innerHTML = 'Uploading...';
const result = await uploadSharedFile(
file,
description,
password,
reference,
message,
activeDays,
function(progress) {
progressBar.style.width = progress + '%';
}
);
// Display result
resultDiv.innerHTML = `
<h2>File Uploaded Successfully!</h2>
<p>Media ID: ${result.media_id}</p>
<p>Download Link: <a href="${result.permalink}" target="_blank">${result.permalink}</a></p>
<p>Shortcode: <code>${result.shortcode}</code></p>
`;
} catch (error) {
resultDiv.innerHTML = `<p style="color: red;">Error: ${error.message}</p>`;
}
});
// Upload function
async function uploadSharedFile(file, description, password, reference = '', message = '', activeDays = 30, 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() + parseInt(activeDays)); // X 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: message,
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;
}
</script>
</body>
</html>
Create a reusable React component for file uploads:
import React, { useState } from 'react';
import axios from 'axios';
const FileUploader = ({ apiKey, apiUrl, onSuccess, onError }) => {
const [file, setFile] = useState(null);
const [description, setDescription] = useState('');
const [message, setMessage] = useState('');
const [password, setPassword] = useState('');
const [reference, setReference] = useState('');
const [activeDays, setActiveDays] = useState(30);
const [progress, setProgress] = useState(0);
const [uploading, setUploading] = useState(false);
const handleFileChange = (e) => {
setFile(e.target.files[0]);
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!file) return;
setUploading(true);
setProgress(0);
try {
// Step 1: Initialize upload
const initResponse = await axios.post(`${apiUrl}/upload/init`, {
original_file_name: file.name,
file_size: file.size
}, {
headers: { 'X-Api-Key': apiKey }
});
const uploadId = initResponse.data.upload_id;
// Step 2: Upload chunks
const chunkSize = 1024 * 1024; // 1MB chunks
const totalChunks = Math.ceil(file.size / chunkSize);
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);
await axios.post(`${apiUrl}/upload/chunk`, formData, {
headers: { 'X-Api-Key': apiKey }
});
setProgress(Math.round((i + 1) / totalChunks * 100));
}
// Step 3: Finalize upload
const activeToDate = new Date();
activeToDate.setDate(activeToDate.getDate() + parseInt(activeDays));
const activeTo = activeToDate.toISOString().slice(0, 19).replace('T', ' ');
const finalizeResponse = await axios.post(`${apiUrl}/upload/finalize`, {
upload_id: uploadId,
total_chunks: totalChunks,
original_file_name: file.name,
description,
message,
password,
reference,
active_to: activeTo
}, {
headers: { 'X-Api-Key': apiKey }
});
setUploading(false);
if (onSuccess) {
onSuccess(finalizeResponse.data);
}
} catch (error) {
setUploading(false);
if (onError) {
onError(error);
}
}
};
return (
<div className="file-uploader">
<form onSubmit={handleSubmit}>
<div className="form-group">
<label>File</label>
<input
type="file"
onChange={handleFileChange}
disabled={uploading}
required
/>
</div>
<div className="form-group">
<label>Description</label>
<input
type="text"
value={description}
onChange={(e) => setDescription(e.target.value)}
disabled={uploading}
required
/>
</div>
<div className="form-group">
<label>Message (Optional)</label>
<textarea
value={message}
onChange={(e) => setMessage(e.target.value)}
disabled={uploading}
/>
</div>
<div className="form-group">
<label>Password</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
disabled={uploading}
required
/>
</div>
<div className="form-group">
<label>Reference (Optional)</label>
<input
type="text"
value={reference}
onChange={(e) => setReference(e.target.value)}
disabled={uploading}
/>
</div>
<div className="form-group">
<label>Active for (days)</label>
<input
type="number"
value={activeDays}
onChange={(e) => setActiveDays(e.target.value)}
min="1"
max="365"
disabled={uploading}
/>
</div>
<button type="submit" disabled={uploading || !file}>
{uploading ? 'Uploading...' : 'Upload File'}
</button>
</form>
{uploading && (
<div className="progress-container">
<div className="progress-bar" style={{ width: `${progress}%` }}></div>
<div className="progress-text">{progress}%</div>
</div>
)}
</div>
);
};
export default FileUploader;
Usage in a React application:
import React, { useState } from 'react';
import FileUploader from './FileUploader';
function App() {
const [uploadResult, setUploadResult] = useState(null);
const [error, setError] = useState(null);
const handleSuccess = (result) => {
setUploadResult(result);
setError(null);
};
const handleError = (error) => {
setError(error.message || 'Upload failed');
setUploadResult(null);
};
return (
<div className="app">
<h1>File Uploader Demo</h1>
<FileUploader
apiKey="your-api-key-here"
apiUrl="https://your-wordpress-site.com/wp-json/qdr-tss/v1"
onSuccess={handleSuccess}
onError={handleError}
/>
{error && (
<div className="error-message">
Error: {error}
</div>
)}
{uploadResult && (
<div className="upload-result">
<h2>File Uploaded Successfully!</h2>
<p>Media ID: {uploadResult.media_id}</p>
<p>
Download Link:
<a href={uploadResult.permalink} target="_blank" rel="noopener noreferrer">
{uploadResult.permalink}
</a>
</p>
<p>Shortcode: <code>{uploadResult.shortcode}</code></p>
</div>
)}
</div>
);
}
export default App;
import React, { useState } from 'react';
import { View, Text, TextInput, Button, StyleSheet, ProgressBar, Alert } from 'react-native';
import DocumentPicker from 'react-native-document-picker';
import RNFS from 'react-native-fs';
const FileUploaderScreen = () => {
const [file, setFile] = useState(null);
const [description, setDescription] = useState('');
const [message, setMessage] = useState('');
const [password, setPassword] = useState('');
const [reference, setReference] = useState('');
const [activeDays, setActiveDays] = useState('30');
const [uploading, setUploading] = useState(false);
const [progress, setProgress] = useState(0);
const API_KEY = 'your-api-key-here';
const API_URL = 'https://your-wordpress-site.com/wp-json/qdr-tss/v1';
const pickDocument = async () => {
try {
const result = await DocumentPicker.pick({
type: [DocumentPicker.types.allFiles],
});
setFile(result[0]);
} catch (err) {
if (DocumentPicker.isCancel(err)) {
// User cancelled the picker
} else {
Alert.alert('Error', 'Error picking document: ' + err.message);
}
}
};
const uploadFile = async () => {
if (!file) {
Alert.alert('Error', 'Please select a file first');
return;
}
if (!description || !password) {
Alert.alert('Error', 'Description and password are required');
return;
}
setUploading(true);
setProgress(0);
try {
// Step 1: Initialize upload
const initResponse = await fetch(`${API_URL}/upload/init`, {
method: 'POST',
headers: {
'X-Api-Key': API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
original_file_name: file.name,
file_size: file.size,
}),
});
const initData = await initResponse.json();
if (!initData.upload_id) {
throw new Error('Failed to initialize upload');
}
const uploadId = initData.upload_id;
// Step 2: Upload chunks
const chunkSize = 1024 * 1024; // 1MB chunks
const totalChunks = Math.ceil(file.size / chunkSize);
const fileContent = await RNFS.readFile(file.uri, 'base64');
for (let i = 0; i < totalChunks; i++) {
const start = i * chunkSize;
const end = Math.min(file.size, start + chunkSize);
// Get chunk data
const chunkData = fileContent.substring(start, end);
// Create a temporary file for the chunk
const tempFilePath = `${RNFS.TemporaryDirectoryPath}/chunk_${i}`;
await RNFS.writeFile(tempFilePath, chunkData, 'base64');
// Upload the chunk
const formData = new FormData();
formData.append('upload_id', uploadId);
formData.append('chunk_index', i.toString());
formData.append('total_chunks', totalChunks.toString());
formData.append('chunk', {
uri: `file://${tempFilePath}`,
name: 'chunk',
type: 'application/octet-stream',
});
const chunkResponse = await fetch(`${API_URL}/upload/chunk`, {
method: 'POST',
headers: {
'X-Api-Key': API_KEY,
'Content-Type': 'multipart/form-data',
},
body: formData,
});
const chunkData = await chunkResponse.json();
if (!chunkData.status || chunkData.status !== 'chunk_uploaded') {
throw new Error(`Failed to upload chunk ${i}`);
}
// Clean up temporary file
await RNFS.unlink(tempFilePath);
// Update progress
setProgress((i + 1) / totalChunks);
}
// Step 3: Finalize upload
const activeToDate = new Date();
activeToDate.setDate(activeToDate.getDate() + parseInt(activeDays));
const activeTo = activeToDate.toISOString().slice(0, 19).replace('T', ' ');
const finalizeResponse = await fetch(`${API_URL}/upload/finalize`, {
method: 'POST',
headers: {
'X-Api-Key': API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
upload_id: uploadId,
total_chunks: totalChunks,
original_file_name: file.name,
description,
message,
password,
reference,
active_to: activeTo,
}),
});
const finalizeData = await finalizeResponse.json();
if (!finalizeData.media_id) {
throw new Error('Failed to finalize upload');
}
setUploading(false);
Alert.alert(
'Success',
`File uploaded successfully!\n\nMedia ID: ${finalizeData.media_id}\nDownload Link: ${finalizeData.permalink}`
);
} catch (error) {
setUploading(false);
Alert.alert('Error', error.message);
}
};
return (
<View style={styles.container}>
<Text style={styles.title}>Upload Shared File</Text>
<Button title="Pick a File" onPress={pickDocument} disabled={uploading} />
{file && (
<Text style={styles.fileInfo}>
Selected: {file.name} ({(file.size / 1024 / 1024).toFixed(2)} MB)
</Text>
)}
<TextInput
style={styles.input}
placeholder="Description"
value={description}
onChangeText={setDescription}
editable={!uploading}
/>
<TextInput
style={styles.input}
placeholder="Message (Optional)"
value={message}
onChangeText={setMessage}
editable={!uploading}
multiline
/>
<TextInput
style={styles.input}
placeholder="Password"
value={password}
onChangeText={setPassword}
editable={!uploading}
secureTextEntry
/>
<TextInput
style={styles.input}
placeholder="Reference (Optional)"
value={reference}
onChangeText={setReference}
editable={!uploading}
/>
<TextInput
style={styles.input}
placeholder="Active for (days)"
value={activeDays}
onChangeText={setActiveDays}
keyboardType="numeric"
editable={!uploading}
/>
{uploading && (
<View style={styles.progressContainer}>
<ProgressBar progress={progress} color="#4CAF50" />
<Text style={styles.progressText}>{Math.round(progress * 100)}%</Text>
</View>
)}
<Button
title={uploading ? 'Uploading...' : 'Upload File'}
onPress={uploadFile}
disabled={uploading || !file}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
},
title: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 20,
},
input: {
borderWidth: 1,
borderColor: '#ccc',
borderRadius: 5,
padding: 10,
marginVertical: 10,
},
fileInfo: {
marginVertical: 10,
fontStyle: 'italic',
},
progressContainer: {
marginVertical: 10,
},
progressText: {
textAlign: 'center',
marginTop: 5,
},
});
export default FileUploaderScreen;
// main.js
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');
const fs = require('fs');
const axios = require('axios');
const FormData = require('form-data');
let mainWindow;
function createWindow() {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
},
});
mainWindow.loadFile('index.html');
}
app.whenReady().then(() => {
createWindow();
});
// API client
const API_KEY = 'your-api-key-here';
const API_URL = 'https://your-wordpress-site.com/wp-json/qdr-tss/v1';
// Handle file upload
ipcMain.handle('upload-file', async (event, options) => {
try {
const { filePath, description, message, password, reference, activeDays } = options;
// Get file info
const fileName = path.basename(filePath);
const fileStats = fs.statSync(filePath);
const fileSize = fileStats.size;
// Step 1: Initialize upload
const initResponse = await axios.post(`${API_URL}/upload/init`, {
original_file_name: fileName,
file_size: fileSize,
}, {
headers: { 'X-Api-Key': API_KEY }
});
const uploadId = initResponse.data.upload_id;
// Step 2: Upload chunks
const chunkSize = 1024 * 1024; // 1MB chunks
const totalChunks = Math.ceil(fileSize / chunkSize);
const fileBuffer = fs.readFileSync(filePath);
for (let i = 0; i < totalChunks; i++) {
const start = i * chunkSize;
const end = Math.min(fileSize, start + chunkSize);
const chunkBuffer = fileBuffer.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', chunkBuffer, { filename: 'chunk' });
await axios.post(`${API_URL}/upload/chunk`, formData, {
headers: {
'X-Api-Key': API_KEY,
...formData.getHeaders(),
}
});
// Update progress
mainWindow.webContents.send('upload-progress', {
progress: Math.round((i + 1) / totalChunks * 100)
});
}
// Step 3: Finalize upload
const activeToDate = new Date();
activeToDate.setDate(activeToDate.getDate() + parseInt(activeDays));
const activeTo = activeToDate.toISOString().slice(0, 19).replace('T', ' ');
const finalizeResponse = await axios.post(`${API_URL}/upload/finalize`, {
upload_id: uploadId,
total_chunks: totalChunks,
original_file_name: fileName,
description,
message,
password,
reference,
active_to: activeTo,
}, {
headers: { 'X-Api-Key': API_KEY }
});
return finalizeResponse.data;
} catch (error) {
console.error('Upload error:', error);
throw new Error(error.response?.data?.message || error.message);
}
});
// preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('api', {
uploadFile: (options) => ipcRenderer.invoke('upload-file', options),
onUploadProgress: (callback) => {
ipcRenderer.on('upload-progress', (event, data) => callback(data));
}
});
// index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>QDR File Uploader</title>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
}
input, textarea {
width: 100%;
padding: 8px;
box-sizing: border-box;
}
.progress {
width: 100%;
height: 20px;
background-color: #f3f3f3;
margin-top: 10px;
display: none;
}
.progress-bar {
height: 100%;
background-color: #4CAF50;
width: 0%;
transition: width 0.3s;
}
#result {
margin-top: 20px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
display: none;
}
</style>
</head>
<body>
<h1>QDR File Uploader</h1>
<form id="upload-form">
<div class="form-group">
<label for="file-path">File Path:</label>
<input type="file" id="file-path" required>
</div>
<div class="form-group">
<label for="description">Description:</label>
<input type="text" id="description" required>
</div>
<div class="form-group">
<label for="message">Message:</label>
<textarea id="message"></textarea>
</div>
<div class="form-group">
<label for="password">Password:</label>
<input type="password" id="password" required>
</div>
<div class="form-group">
<label for="reference">Reference:</label>
<input type="text" id="reference">
</div>
<div class="form-group">
<label for="active-days">Active for (days):</label>
<input type="number" id="active-days" value="30" min="1" max="365">
</div>
<button type="submit">Upload</button>
</form>
<div class="progress" id="progress-container">
<div class="progress-bar" id="progress-bar"></div>
</div>
<div id="result"></div>
<script>
document.getElementById('upload-form').addEventListener('submit', async function(e) {
e.preventDefault();
const fileInput = document.getElementById('file-path');
if (!fileInput.files.length) {
alert('Please select a file');
return;
}
const description = document.getElementById('description').value;
const message = document.getElementById('message').value;
const password = document.getElementById('password').value;
const reference = document.getElementById('reference').value;
const activeDays = document.getElementById('active-days').value;
// Show progress
const progressContainer = document.getElementById('progress-container');
const progressBar = document.getElementById('progress-bar');
const resultDiv = document.getElementById('result');
progressContainer.style.display = 'block';
progressBar.style.width = '0%';
resultDiv.style.display = 'none';
// Disable form
const form = document.getElementById('upload-form');
form.querySelectorAll('input, textarea, button').forEach(el => {
el.disabled = true;
});
try {
const result = await window.api.uploadFile({
filePath: fileInput.files[0].path,
description,
message,
password,
reference,
activeDays
});
// Display result
resultDiv.innerHTML = `
<h2>File Uploaded Successfully!</h2>
<p>Media ID: ${result.media_id}</p>
<p>Download Link: <a href="${result.permalink}" target="_blank">${result.permalink}</a></p>
<p>Shortcode: <code>${result.shortcode}</code></p>
`;
resultDiv.style.display = 'block';
} catch (error) {
resultDiv.innerHTML = `<p style="color: red;">Error: ${error.message}</p>`;
resultDiv.style.display = 'block';
} finally {
// Re-enable form
form.querySelectorAll('input, textarea, button').forEach(el => {
el.disabled = false;
});
}
});
// Update progress bar
window.api.onUploadProgress(function(data) {
document.getElementById('progress-bar').style.width = data.progress + '%';
});
</script>
</body>
</html>
You can integrate the plugin with Salesforce using Apex code to upload files and store the reference in Salesforce records.
public class QDRFileUploaderController {
// API Configuration
private static final String API_URL = 'https://your-wordpress-site.com/wp-json/qdr-tss/v1';
private static final String API_KEY = 'your-api-key-here';
// Upload a file to QDR Temporary Shared Storage
@AuraEnabled
public static Map<String, String> uploadFile(String fileName, String fileContent, String description,
String message, String password, String reference, Integer activeDays) {
try {
Blob fileBlob = EncodingUtil.base64Decode(fileContent);
Integer fileSize = fileBlob.size();
// Step 1: Initialize upload
HttpRequest initRequest = new HttpRequest();
initRequest.setEndpoint(API_URL + '/upload/init');
initRequest.setMethod('POST');
initRequest.setHeader('X-Api-Key', API_KEY);
initRequest.setHeader('Content-Type', 'application/json');
Map<String, Object> initParams = new Map<String, Object>();
initParams.put('original_file_name', fileName);
initParams.put('file_size', fileSize);
initRequest.setBody(JSON.serialize(initParams));
Http http = new Http();
HttpResponse initResponse = http.send(initRequest);
if (initResponse.getStatusCode() != 200) {
throw new AuraHandledException('Failed to initialize upload: ' + initResponse.getBody());
}
Map<String, Object> initResult = (Map<String, Object>) JSON.deserializeUntyped(initResponse.getBody());
String uploadId = (String) initResult.get('upload_id');
// Step 2: Upload chunks
Integer chunkSize = 1024 * 1024; // 1MB chunks
Integer totalChunks = Math.ceil(fileSize / (Double) chunkSize).intValue();
for (Integer i = 0; i < totalChunks; i++) {
Integer start = i * chunkSize;
Integer end = Math.min(fileSize, start + chunkSize);
// Create chunk
Blob chunkBlob = fileBlob.substring(start, end);
String chunkBase64 = EncodingUtil.base64Encode(chunkBlob);
// Upload chunk
HttpRequest chunkRequest = new HttpRequest();
chunkRequest.setEndpoint(API_URL + '/upload/chunk');
chunkRequest.setMethod('POST');
chunkRequest.setHeader('X-Api-Key', API_KEY);
String boundary = '---------------------------' + String.valueOf(DateTime.now().getTime());
chunkRequest.setHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
String body = '';
body += '--' + boundary + '\r\n';
body += 'Content-Disposition: form-data; name="upload_id"\r\n\r\n';
body += uploadId + '\r\n';
body += '--' + boundary + '\r\n';
body += 'Content-Disposition: form-data; name="chunk_index"\r\n\r\n';
body += String.valueOf(i) + '\r\n';
body += '--' + boundary + '\r\n';
body += 'Content-Disposition: form-data; name="total_chunks"\r\n\r\n';
body += String.valueOf(totalChunks) + '\r\n';
body += '--' + boundary + '\r\n';
body += 'Content-Disposition: form-data; name="chunk"; filename="chunk"\r\n';
body += 'Content-Type: application/octet-stream\r\n\r\n';
String bodyEnd = '\r\n--' + boundary + '--\r\n';
Blob bodyBlob = Blob.valueOf(body);
Blob bodyEndBlob = Blob.valueOf(bodyEnd);
// Join the blobs
Blob fullBody = Blob.valueOf('');
fullBody = Blob.valueOf(EncodingUtil.base64Encode(bodyBlob) +
EncodingUtil.base64Encode(chunkBlob) +
EncodingUtil.base64Encode(bodyEndBlob));
chunkRequest.setBodyAsBlob(fullBody);
HttpResponse chunkResponse = http.send(chunkRequest);
if (chunkResponse.getStatusCode() != 200) {
throw new AuraHandledException('Failed to upload chunk ' + i + ': ' + chunkResponse.getBody());
}
}
// Step 3: Finalize upload
HttpRequest finalizeRequest = new HttpRequest();
finalizeRequest.setEndpoint(API_URL + '/upload/finalize');
finalizeRequest.setMethod('POST');
finalizeRequest.setHeader('X-Api-Key', API_KEY);
finalizeRequest.setHeader('Content-Type', 'application/json');
// Set expiration date
Datetime activeToDate = Datetime.now();
activeToDate = activeToDate.addDays(activeDays);
String activeTo = activeToDate.format('yyyy-MM-dd HH:mm:ss');
Map<String, Object> finalizeParams = new Map<String, Object>();
finalizeParams.put('upload_id', uploadId);
finalizeParams.put('total_chunks', totalChunks);
finalizeParams.put('original_file_name', fileName);
finalizeParams.put('description', description);
finalizeParams.put('message', message);
finalizeParams.put('password', password);
finalizeParams.put('reference', reference);
finalizeParams.put('active_to', activeTo);
finalizeRequest.setBody(JSON.serialize(finalizeParams));
HttpResponse finalizeResponse = http.send(finalizeRequest);
if (finalizeResponse.getStatusCode() != 201) {
throw new AuraHandledException('Failed to finalize upload: ' + finalizeResponse.getBody());
}
Map<String, Object> finalResult = (Map<String, Object>) JSON.deserializeUntyped(finalizeResponse.getBody());
Map<String, String> result = new Map<String, String>();
result.put('mediaId', (String) finalResult.get('media_id'));
result.put('permalink', (String) finalResult.get('permalink'));
result.put('shortcode', (String) finalResult.get('shortcode'));
return result;
} catch (Exception e) {
throw new AuraHandledException('Error uploading file: ' + e.getMessage());
}
}
// Save shared file info to a Salesforce record
@AuraEnabled
public static void saveSharedFileInfo(Id recordId, String objectName, String mediaId, String permalink) {
try {
// Create a custom object to store the shared file info
SObject sharedFile = Schema.getGlobalDescribe().get('Shared_File__c').newSObject();
sharedFile.put('Media_ID__c', mediaId);
sharedFile.put('Permalink__c', permalink);
sharedFile.put('Related_Record__c', recordId);
sharedFile.put('Object_Type__c', objectName);
insert sharedFile;
} catch (Exception e) {
throw new AuraHandledException('Error saving shared file info: ' + e.getMessage());
}
}
}
<?php
/**
* QDR Temporary Shared Storage - Document Management System Integration
*
* This script demonstrates how to integrate the QDR Temporary Shared Storage plugin
* with a Document Management System. It monitors a folder for new files, uploads them
* to the WordPress site via the REST API, and stores the media information in a database.
*/
// Configuration
$config = [
'api_key' => 'your-api-key-here',
'api_url' => 'https://your-wordpress-site.com/wp-json/qdr-tss/v1',
'watch_folder' => '/path/to/watch/folder',
'processed_folder' => '/path/to/processed/folder',
'log_file' => '/path/to/log/file.log',
'db_host' => 'localhost',
'db_name' => 'dms_database',
'db_user' => 'db_user',
'db_pass' => 'db_password',
];
// Initialize database connection
$db = new mysqli($config['db_host'], $config['db_user'], $config['db_pass'], $config['db_name']);
if ($db->connect_error) {
log_message("Database connection failed: " . $db->connect_error);
exit;
}
// Create the shared_files table if it doesn't exist
$db->query("
CREATE TABLE IF NOT EXISTS shared_files (
id INT AUTO_INCREMENT PRIMARY KEY,
file_path VARCHAR(255) NOT NULL,
original_file_name VARCHAR(255) NOT NULL,
media_id VARCHAR(255) NOT NULL,
permalink TEXT NOT NULL,
shortcode TEXT NOT NULL,
password VARCHAR(255) NOT NULL,
reference VARCHAR(255),
upload_date DATETIME DEFAULT CURRENT_TIMESTAMP,
expiry_date DATETIME NOT NULL,
document_type VARCHAR(50),
document_id VARCHAR(50),
notes TEXT
)
");
// Get all files in the watch folder
$files = glob($config['watch_folder'] . '/*.*');
foreach ($files as $file_path) {
$file_name = basename($file_path);
log_message("Processing file: " . $file_name);
// Get metadata from the file name
// Assuming file names follow a pattern like "DOCTYPE_DOCID_REFERENCE.pdf"
$file_parts = explode('_', pathinfo($file_name, PATHINFO_FILENAME));
$document_type = isset($file_parts[0]) ? $file_parts[0] : '';
$document_id = isset($file_parts[1]) ? $file_parts[1] : '';
$reference = isset($file_parts[2]) ? $file_parts[2] : '';
// Generate a random password
$password = generate_random_password();
try {
// Upload the file
$result = upload_file(
$file_path,
"Document: $document_type - $document_id",
"This file was automatically uploaded by the DMS integration.",
$password,
$reference
);
// Store the result in the database
$stmt = $db->prepare("INSERT INTO shared_files (
file_path, original_file_name, media_id, permalink, shortcode,
password, reference, expiry_date, document_type, document_id
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
$expiry_date = date('Y-m-d H:i:s', strtotime('+30 days'));
$stmt->bind_param(
'ssssssssss',
$file_path,
$file_name,
$result['media_id'],
$result['permalink'],
$result['shortcode'],
$password,
$reference,
$expiry_date,
$document_type,
$document_id
);
$stmt->execute();
if ($stmt->affected_rows > 0) {
log_message("File uploaded and recorded in the database.");
// Move the file to the processed folder
$processed_path = $config['processed_folder'] . '/' . $file_name;
rename($file_path, $processed_path);
log_message("File moved to processed folder.");
} else {
log_message("Failed to record file in the database.");
}
$stmt->close();
} catch (Exception $e) {
log_message("Error: " . $e->getMessage());
}
}
$db->close();
/**
* Upload a file using the QDR Temporary Shared Storage REST API
*/
function upload_file($file_path, $description, $message, $password, $reference) {
global $config;
// 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($config['api_url'] . '/upload/init');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-Api-Key: ' . $config['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($config['api_url'] . '/upload/chunk');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-Api-Key: ' . $config['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);
}
log_message("Uploaded chunk " . ($i + 1) . " of " . $total_chunks);
}
fclose($file);
// Step 3: Finalize upload
$ch = curl_init($config['api_url'] . '/upload/finalize');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-Api-Key: ' . $config['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' => $message,
'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;
}
/**
* Generate a random password
*/
function generate_random_password($length = 12) {
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+';
$password = '';
for ($i = 0; $i < $length; $i++) {
$password .= $chars[rand(0, strlen($chars) - 1)];
}
return $password;
}
/**
* Log a message to the log file
*/
function log_message($message) {
global $config;
$date = date('Y-m-d H:i:s');
$log_message = "[$date] $message\n";
file_put_contents($config['log_file'], $log_message, FILE_APPEND);
echo $log_message;
}
<?php
/**
* QDR Temporary Shared Storage - Email Integration
*
* This script demonstrates how to send an email with a link to download a file
* from the QDR Temporary Shared Storage plugin.
*/
// Require PHPMailer
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require 'vendor/autoload.php';
/**
* Upload a file to QDR Temporary Shared Storage and send an email with the link
*
* @param string $file_path Path to the file to upload
* @param string $description Description of the file
* @param string $message Custom message
* @param string $recipient_email Email address of the recipient
* @param string $recipient_name Name of the recipient
* @param string $email_subject Subject of the email
* @param string $email_body Body of the email (HTML)
* @return array Upload result
*/
function send_file_by_email($file_path, $description, $message, $recipient_email, $recipient_name, $email_subject, $email_body) {
// Generate a random password
$password = generate_random_password();
// Generate a unique reference code
$reference = uniqid('EMAIL-');
// Upload the file
$result = upload_file($file_path, $description, $message, $password, $reference);
// Prepare email content
$download_link = $result['permalink'];
$media_id = $result['media_id'];
// Replace placeholders in the email body
$email_body = str_replace('{{DOWNLOAD_LINK}}', $download_link, $email_body);
$email_body = str_replace('{{PASSWORD}}', $password, $email_body);
$email_body = str_replace('{{REFERENCE}}', $reference, $email_body);
$email_body = str_replace('{{FILE_NAME}}', basename($file_path), $email_body);
$email_body = str_replace('{{EXPIRY_DATE}}', date('Y-m-d', strtotime('+30 days')), $email_body);
// Send the email
send_email($recipient_email, $recipient_name, $email_subject, $email_body);
return $result;
}
/**
* Send an email using PHPMailer
*/
function send_email($to_email, $to_name, $subject, $body) {
$mail = new PHPMailer(true);
try {
// Server settings
$mail->isSMTP();
$mail->Host = 'smtp.example.com';
$mail->SMTPAuth = true;
$mail->Username = 'user@example.com';
$mail->Password = 'password';
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
// Recipients
$mail->setFrom('from@example.com', 'Your Company');
$mail->addAddress($to_email, $to_name);
$mail->addReplyTo('info@example.com', 'Information');
// Content
$mail->isHTML(true);
$mail->Subject = $subject;
$mail->Body = $body;
$mail->AltBody = strip_tags($body);
$mail->send();
return true;
} catch (Exception $e) {
throw new Exception('Message could not be sent. Mailer Error: ' . $mail->ErrorInfo);
}
}
/**
* Upload a file using the QDR Temporary Shared Storage REST API
*/
function upload_file($file_path, $description, $message, $password, $reference) {
$api_key = 'your-api-key-here';
$api_url = 'https://your-wordpress-site.com/wp-json/qdr-tss/v1';
// Implement the file upload logic (same as in previous examples)
// ...
}
/**
* Generate a random password
*/
function generate_random_password($length = 12) {
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+';
$password = '';
for ($i = 0; $i < $length; $i++) {
$password .= $chars[rand(0, strlen($chars) - 1)];
}
return $password;
}
// Example usage
try {
$file_path = '/path/to/document.pdf';
$description = 'Important Document';
$message = 'Please review this document and provide feedback.';
$recipient_email = 'client@example.com';
$recipient_name = 'John Doe';
$email_subject = 'Important Document for Review';
$email_body = '
<html>
<body>
<h2>Important Document</h2>
<p>Dear {{RECIPIENT_NAME}},</p>
<p>We have shared an important document with you for review.</p>
<p>You can download the document using the following information:</p>
<ul>
<li><strong>Download Link:</strong> <a href="{{DOWNLOAD_LINK}}">{{FILE_NAME}}</a></li>
<li><strong>Password:</strong> {{PASSWORD}}</li>
<li><strong>Reference:</strong> {{REFERENCE}}</li>
</ul>
<p>The link will be active until {{EXPIRY_DATE}}.</p>
<p>If you have any questions, please contact us.</p>
<p>Best regards,<br>Your Company</p>
</body>
</html>
';
$email_body = str_replace('{{RECIPIENT_NAME}}', $recipient_name, $email_body);
$result = send_file_by_email($file_path, $description, $message, $recipient_email, $recipient_name, $email_subject, $email_body);
echo "File uploaded and email sent successfully!\n";
echo "Media ID: " . $result['media_id'] . "\n";
echo "Permalink: " . $result['permalink'] . "\n";
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}
You can integrate the plugin with Zapier to automate workflows. Here's an example Zapier integration script:
// Zapier Code for QDR Temporary Shared Storage Integration
// When a new file is added to Google Drive (Trigger)
const inputData = {
fileUrl: inputData.fileUrl,
fileName: inputData.fileName,
fileId: inputData.fileId,
mimeType: inputData.mimeType
};
// Download the file content
const fileResponse = await fetch(inputData.fileUrl, {
method: 'GET',
headers: {
'Authorization': `Bearer ${bundle.authData.access_token}`
}
});
// Get file as buffer
const fileBuffer = await fileResponse.buffer();
// Upload file to QDR Temporary Shared Storage
const API_KEY = 'your-api-key-here';
const API_URL = 'https://your-wordpress-site.com/wp-json/qdr-tss/v1';
// Step 1: Initialize upload
const initResponse = await fetch(`${API_URL}/upload/init`, {
method: 'POST',
headers: {
'X-Api-Key': API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
original_file_name: inputData.fileName,
file_size: fileBuffer.length
})
});
const initData = await initResponse.json();
if (!initData.upload_id) {
throw new Error('Failed to initialize upload');
}
const uploadId = initData.upload_id;
// Step 2: Upload chunks
const chunkSize = 1024 * 1024; // 1MB chunks
const totalChunks = Math.ceil(fileBuffer.length / chunkSize);
for (let i = 0; i < totalChunks; i++) {
const start = i * chunkSize;
const end = Math.min(fileBuffer.length, start + chunkSize);
const chunk = fileBuffer.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', new Blob([chunk], { type: 'application/octet-stream' }), 'chunk');
const chunkResponse = await fetch(`${API_URL}/upload/chunk`, {
method: 'POST',
headers: {
'X-Api-Key': API_KEY
},
body: formData
});
const chunkData = await chunkResponse.json();
if (!chunkData.status || chunkData.status !== 'chunk_uploaded') {
throw new Error(`Failed to upload chunk ${i}`);
}
}
// Step 3: Finalize upload
const description = `File uploaded from Google Drive: ${inputData.fileName}`;
const message = 'This file was automatically uploaded via Zapier.';
const password = generatePassword();
const reference = `ZAPIER-${Date.now()}`;
const activeTo = new Date();
activeTo.setDate(activeTo.getDate() + 30); // 30 days from now
const finalizeResponse = await fetch(`${API_URL}/upload/finalize`, {
method: 'POST',
headers: {
'X-Api-Key': API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
upload_id: uploadId,
total_chunks: totalChunks,
original_file_name: inputData.fileName,
description,
message,
password,
reference,
active_to: activeTo.toISOString().slice(0, 19).replace('T', ' ')
})
});
const finalizeData = await finalizeResponse.json();
if (!finalizeData.media_id) {
throw new Error('Failed to finalize upload');
}
// Generate password function
function generatePassword(length = 12) {
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+';
let password = '';
for (let i = 0; i < length; i++) {
password += chars.charAt(Math.floor(Math.random() * chars.length));
}
return password;
}
// Return data for next Zapier step
return {
media_id: finalizeData.media_id,
permalink: finalizeData.permalink,
shortcode: finalizeData.shortcode,
password: password,
reference: reference,
expiry_date: activeTo.toISOString().slice(0, 10)
};
The QDR Temporary Shared Storage plugin's REST API enables integration with a wide variety of applications and systems. By following the examples in this guide, you can easily incorporate secure file sharing capabilities into your existing workflows and applications.
For further assistance or custom integration solutions, please contact the plugin author or refer to the API documentation in the plugin's readme file.