feat: add cover image field to album creation and editing
- Added dedicated cover image upload field in create_album.php form - Display current cover image preview when editing - Drag-and-drop support for cover image with real-time preview - Shows filename and file size after selection - Updated save_album.php to handle cover image upload - Updated update_album.php to handle cover image replacement - Deletes old cover image when updating - Cover image optional - first photo in album used as fallback - Recommended cover dimensions: 500x500px or larger (square) - File validation: max 5MB, supports JPG, PNG, GIF, WEBP - All cover image changes included in transaction with rollback on error
This commit is contained in:
@@ -208,6 +208,27 @@ require_once($rootPath . '/components/banner.php');
|
||||
color: #999;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.cover-preview-area {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.current-cover {
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.current-cover img {
|
||||
width: 100%;
|
||||
max-height: 250px;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#coverUploadArea {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
||||
<section class="tour-list-page py-100 rel">
|
||||
@@ -234,6 +255,27 @@ require_once($rootPath . '/components/banner.php');
|
||||
<div class="helper-text">Optional: Share details about when, where, or why you created this album</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="cover_image">Album Cover Image</label>
|
||||
<div class="cover-preview-area">
|
||||
<?php if ($album && $album['cover_image']): ?>
|
||||
<div class="current-cover">
|
||||
<img id="currentCoverImg" src="<?php echo htmlspecialchars($album['cover_image']); ?>" alt="Current cover">
|
||||
<p style="margin-top: 10px; font-size: 0.9rem; color: #666;">Current cover image</p>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div id="currentCoverImg" style="width: 100%; height: 200px; background: #f0f0f0; border-radius: 6px; display: flex; align-items: center; justify-content: center; color: #999; margin-bottom: 15px;">No cover image yet</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="upload-area" id="coverUploadArea" style="margin-top: 15px;">
|
||||
<input type="file" id="cover_image" name="cover_image" accept="image/*" class="upload-input">
|
||||
<div style="font-size: 1.5rem; margin-bottom: 10px;">🖼️</div>
|
||||
<p class="upload-text">Click to select cover image</p>
|
||||
<div class="helper-text">Image will be used as album thumbnail. Recommended: Square image (500x500px or larger)</div>
|
||||
</div>
|
||||
<div id="coverFileName" style="margin-top: 10px;"></div>
|
||||
</div>
|
||||
|
||||
<?php if ($album): ?>
|
||||
<div class="photos-section">
|
||||
<h4>Photos in Album</h4>
|
||||
@@ -280,8 +322,60 @@ require_once($rootPath . '/components/banner.php');
|
||||
const uploadArea = document.getElementById('uploadArea');
|
||||
const fileInput = document.getElementById('photos');
|
||||
const fileList = document.getElementById('fileList');
|
||||
const coverUploadArea = document.getElementById('coverUploadArea');
|
||||
const coverImageInput = document.getElementById('cover_image');
|
||||
const coverFileName = document.getElementById('coverFileName');
|
||||
|
||||
// Drag and drop
|
||||
// Cover image handling
|
||||
coverUploadArea.addEventListener('click', () => {
|
||||
coverImageInput.click();
|
||||
});
|
||||
|
||||
coverImageInput.addEventListener('change', (e) => {
|
||||
if (e.target.files.length > 0) {
|
||||
const file = e.target.files[0];
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = (event) => {
|
||||
const preview = document.getElementById('currentCoverImg');
|
||||
if (preview.tagName === 'IMG') {
|
||||
preview.src = event.target.result;
|
||||
} else {
|
||||
const img = document.createElement('img');
|
||||
img.src = event.target.result;
|
||||
img.alt = 'Cover preview';
|
||||
preview.replaceWith(img);
|
||||
img.id = 'currentCoverImg';
|
||||
}
|
||||
};
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
|
||||
coverFileName.innerHTML = '<p style="color: #667eea; font-weight: 500;">Selected: ' + file.name + ' (' + (file.size / 1024 / 1024).toFixed(2) + ' MB)</p>';
|
||||
}
|
||||
});
|
||||
|
||||
// Drag and drop for cover
|
||||
coverUploadArea.addEventListener('dragover', (e) => {
|
||||
e.preventDefault();
|
||||
coverUploadArea.classList.add('dragover');
|
||||
});
|
||||
|
||||
coverUploadArea.addEventListener('dragleave', () => {
|
||||
coverUploadArea.classList.remove('dragover');
|
||||
});
|
||||
|
||||
coverUploadArea.addEventListener('drop', (e) => {
|
||||
e.preventDefault();
|
||||
coverUploadArea.classList.remove('dragover');
|
||||
if (e.dataTransfer.files.length > 0) {
|
||||
coverImageInput.files = e.dataTransfer.files;
|
||||
const event = new Event('change', { bubbles: true });
|
||||
coverImageInput.dispatchEvent(event);
|
||||
}
|
||||
});
|
||||
|
||||
// Regular photos drag and drop
|
||||
uploadArea.addEventListener('dragover', (e) => {
|
||||
e.preventDefault();
|
||||
uploadArea.classList.add('dragover');
|
||||
|
||||
@@ -58,6 +58,43 @@ try {
|
||||
}
|
||||
}
|
||||
|
||||
// Handle cover image upload
|
||||
$coverImagePath = null;
|
||||
if (isset($_FILES['cover_image']) && $_FILES['cover_image']['error'] !== UPLOAD_ERR_NO_FILE) {
|
||||
$allowedMimes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
|
||||
$maxSize = 5 * 1024 * 1024; // 5MB
|
||||
|
||||
$fileName = $_FILES['cover_image']['name'];
|
||||
$fileTmpName = $_FILES['cover_image']['tmp_name'];
|
||||
$fileSize = $_FILES['cover_image']['size'];
|
||||
$fileMime = mime_content_type($fileTmpName);
|
||||
|
||||
// Validate file
|
||||
if (!in_array($fileMime, $allowedMimes)) {
|
||||
throw new Exception('Invalid cover image file type');
|
||||
}
|
||||
|
||||
if ($fileSize > $maxSize) {
|
||||
throw new Exception('Cover image file too large (max 5MB)');
|
||||
}
|
||||
|
||||
// Generate unique filename
|
||||
$ext = pathinfo($fileName, PATHINFO_EXTENSION);
|
||||
$newFileName = 'cover_' . uniqid() . '.' . $ext;
|
||||
$filePath = $albumDir . '/' . $newFileName;
|
||||
$coverImagePath = '/assets/uploads/gallery/' . $album_id . '/' . $newFileName;
|
||||
|
||||
if (!move_uploaded_file($fileTmpName, $filePath)) {
|
||||
throw new Exception('Failed to upload cover image');
|
||||
}
|
||||
|
||||
// Update cover image in album record
|
||||
$updateCover = $conn->prepare("UPDATE photo_albums SET cover_image = ? WHERE album_id = ?");
|
||||
$updateCover->bind_param("si", $coverImagePath, $album_id);
|
||||
$updateCover->execute();
|
||||
$updateCover->close();
|
||||
}
|
||||
|
||||
// Handle photo uploads
|
||||
if (isset($_FILES['photos']) && $_FILES['photos']['error'][0] !== UPLOAD_ERR_NO_FILE) {
|
||||
$allowedMimes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
|
||||
@@ -95,8 +132,8 @@ try {
|
||||
throw new Exception('Failed to upload: ' . $fileName);
|
||||
}
|
||||
|
||||
// Set first photo as cover
|
||||
if ($firstPhoto) {
|
||||
// Set first photo as cover if no cover image was uploaded
|
||||
if ($firstPhoto && !$coverImagePath) {
|
||||
$updateCover = $conn->prepare("UPDATE photo_albums SET cover_image = ? WHERE album_id = ?");
|
||||
$updateCover->bind_param("si", $relativePath, $album_id);
|
||||
$updateCover->execute();
|
||||
|
||||
@@ -75,6 +75,60 @@ try {
|
||||
$updateStmt->execute();
|
||||
$updateStmt->close();
|
||||
|
||||
// Handle cover image upload if provided
|
||||
if (isset($_FILES['cover_image']) && $_FILES['cover_image']['error'] !== UPLOAD_ERR_NO_FILE) {
|
||||
$allowedMimes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
|
||||
$maxSize = 5 * 1024 * 1024; // 5MB
|
||||
|
||||
$fileName = $_FILES['cover_image']['name'];
|
||||
$fileTmpName = $_FILES['cover_image']['tmp_name'];
|
||||
$fileSize = $_FILES['cover_image']['size'];
|
||||
$fileMime = mime_content_type($fileTmpName);
|
||||
|
||||
// Validate file
|
||||
if (!in_array($fileMime, $allowedMimes)) {
|
||||
throw new Exception('Invalid cover image file type');
|
||||
}
|
||||
|
||||
if ($fileSize > $maxSize) {
|
||||
throw new Exception('Cover image file too large (max 5MB)');
|
||||
}
|
||||
|
||||
$albumDir = $rootPath . '/assets/uploads/gallery/' . $album_id;
|
||||
|
||||
// Delete old cover if it exists
|
||||
$oldCoverStmt = $conn->prepare("SELECT cover_image FROM photo_albums WHERE album_id = ?");
|
||||
$oldCoverStmt->bind_param("i", $album_id);
|
||||
$oldCoverStmt->execute();
|
||||
$oldCoverResult = $oldCoverStmt->get_result();
|
||||
if ($oldCoverResult->num_rows > 0) {
|
||||
$oldCover = $oldCoverResult->fetch_assoc();
|
||||
if ($oldCover['cover_image']) {
|
||||
$oldCoverPath = $_SERVER['DOCUMENT_ROOT'] . $oldCover['cover_image'];
|
||||
if (file_exists($oldCoverPath)) {
|
||||
unlink($oldCoverPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
$oldCoverStmt->close();
|
||||
|
||||
// Generate unique filename
|
||||
$ext = pathinfo($fileName, PATHINFO_EXTENSION);
|
||||
$newFileName = 'cover_' . uniqid() . '.' . $ext;
|
||||
$filePath = $albumDir . '/' . $newFileName;
|
||||
$coverImagePath = '/assets/uploads/gallery/' . $album_id . '/' . $newFileName;
|
||||
|
||||
if (!move_uploaded_file($fileTmpName, $filePath)) {
|
||||
throw new Exception('Failed to upload cover image');
|
||||
}
|
||||
|
||||
// Update cover image in album record
|
||||
$updateCover = $conn->prepare("UPDATE photo_albums SET cover_image = ? WHERE album_id = ?");
|
||||
$updateCover->bind_param("si", $coverImagePath, $album_id);
|
||||
$updateCover->execute();
|
||||
$updateCover->close();
|
||||
}
|
||||
|
||||
// Handle photo uploads if any
|
||||
if (isset($_FILES['photos']) && $_FILES['photos']['error'][0] !== UPLOAD_ERR_NO_FILE) {
|
||||
$allowedMimes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
|
||||
|
||||
Reference in New Issue
Block a user