Ver código fonte

Modify image gallery functions

svalavuo 1 mês atrás
pai
commit
d49659db2c
4 arquivos alterados com 332 adições e 35 exclusões
  1. 108 4
      admin/upload_image.php
  2. 116 0
      css/wysiwyg.css
  3. 5 0
      database_migrations/add_thumbnail_column.sql
  4. 103 31
      js/wysiwyg.js

+ 108 - 4
admin/upload_image.php

@@ -22,9 +22,13 @@ header('Content-Type: application/json');
 
 // Create uploads directory if it doesn't exist
 $uploadsDir = '../uploads/images';
+$thumbsDir = '../uploads/images/thumbs';
 if (!file_exists($uploadsDir)) {
     mkdir($uploadsDir, 0755, true);
 }
+if (!file_exists($thumbsDir)) {
+    mkdir($thumbsDir, 0755, true);
+}
 
 // Handle file upload
 if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['images'])) {
@@ -52,11 +56,16 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['images'])) {
                 $extension = pathinfo($file['name'], PATHINFO_EXTENSION);
                 $filename = uniqid() . '.' . $extension;
                 $filepath = $uploadsDir . '/' . $filename;
+                $thumbFilename = 'thumb_' . $filename;
+                $thumbPath = $thumbsDir . '/' . $thumbFilename;
                 
                 // Move file to uploads directory
                 if (move_uploaded_file($file['tmp_name'], $filepath)) {
+                    // Generate thumbnail
+                    $thumbnailGenerated = generateThumbnail($filepath, $thumbPath);
+                    
                     // Save to database
-                    $imageId = saveImageToDatabase($filename, $file['name'], $file['size']);
+                    $imageId = saveImageToDatabase($filename, $file['name'], $file['size'], $thumbFilename);
                     
                     if ($imageId) {
                         $uploadedFiles[] = [
@@ -65,11 +74,14 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['images'])) {
                             'original_name' => $file['name'],
                             'size' => $file['size'],
                             'url' => '/uploads/images/' . $filename,
-                            'thumbnail_url' => '/uploads/images/' . $filename
+                            'thumbnail_url' => '/uploads/images/thumbs/' . $thumbFilename
                         ];
                     } else {
                         $errors[] = 'Failed to save ' . $file['name'] . ' to database';
                         unlink($filepath); // Remove uploaded file
+                        if ($thumbnailGenerated) {
+                            unlink($thumbPath); // Remove thumbnail if generated
+                        }
                     }
                 } else {
                     $errors[] = 'Failed to upload ' . $file['name'];
@@ -138,15 +150,101 @@ function validateImageFile($file) {
     return ['valid' => true];
 }
 
+/**
+ * Generate thumbnail for uploaded image
+ */
+function generateThumbnail($sourcePath, $destPath, $maxWidth = 300, $maxHeight = 200) {
+    // Check if GD extension is available
+    if (!extension_loaded('gd')) {
+        error_log('GD extension not available for thumbnail generation');
+        return false;
+    }
+    
+    try {
+        // Get image info
+        $imageInfo = getimagesize($sourcePath);
+        if (!$imageInfo) {
+            return false;
+        }
+        
+        $width = $imageInfo[0];
+        $height = $imageInfo[1];
+        $type = $imageInfo[2];
+        
+        // Calculate new dimensions
+        $ratio = min($maxWidth / $width, $maxHeight / $height);
+        $newWidth = (int)($width * $ratio);
+        $newHeight = (int)($height * $ratio);
+        
+        // Create image resource based on type
+        switch ($type) {
+            case IMAGETYPE_JPEG:
+                $source = imagecreatefromjpeg($sourcePath);
+                break;
+            case IMAGETYPE_PNG:
+                $source = imagecreatefrompng($sourcePath);
+                break;
+            case IMAGETYPE_GIF:
+                $source = imagecreatefromgif($sourcePath);
+                break;
+            default:
+                return false;
+        }
+        
+        if (!$source) {
+            return false;
+        }
+        
+        // Create new image
+        $thumb = imagecreatetruecolor($newWidth, $newHeight);
+        
+        // Preserve transparency for PNG and GIF
+        if ($type == IMAGETYPE_PNG || $type == IMAGETYPE_GIF) {
+            imagealphablending($thumb, false);
+            imagesavealpha($thumb, true);
+            $transparent = imagecolorallocatealpha($thumb, 255, 255, 255, 127);
+            imagefilledrectangle($thumb, 0, 0, $newWidth, $newHeight, $transparent);
+        }
+        
+        // Resize image
+        imagecopyresampled($thumb, $source, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);
+        
+        // Save thumbnail
+        $result = false;
+        switch ($type) {
+            case IMAGETYPE_JPEG:
+                $result = imagejpeg($thumb, $destPath, 85);
+                break;
+            case IMAGETYPE_PNG:
+                $result = imagepng($thumb, $destPath, 8);
+                break;
+            case IMAGETYPE_GIF:
+                $result = imagegif($thumb, $destPath);
+                break;
+        }
+        
+        // Clean up
+        imagedestroy($source);
+        imagedestroy($thumb);
+        
+        return $result;
+        
+    } catch (Exception $e) {
+        error_log('Error generating thumbnail: ' . $e->getMessage());
+        return false;
+    }
+}
+
 /**
  * Save image information to database
  */
-function saveImageToDatabase($filename, $originalName, $size) {
+function saveImageToDatabase($filename, $originalName, $size, $thumbnailFilename = null) {
     $db = Database::getInstance();
     
     try {
         $db->insert('images', [
             'filename' => $filename,
+            'thumbnail_filename' => $thumbnailFilename,
             'original_name' => $originalName,
             'file_size' => $size,
             'uploaded_at' => date('Y-m-d H:i:s'),
@@ -171,13 +269,19 @@ function getExistingImages() {
         
         $result = [];
         foreach ($images as $image) {
+            // Use thumbnail if available, otherwise use original image
+            $thumbnailUrl = $image['thumbnail_filename'] 
+                ? '/uploads/images/thumbs/' . $image['thumbnail_filename']
+                : '/uploads/images/' . $image['filename'];
+                
             $result[] = [
                 'id' => $image['id'],
                 'filename' => $image['filename'],
+                'thumbnail_filename' => $image['thumbnail_filename'],
                 'original_name' => $image['original_name'],
                 'size' => $image['file_size'],
                 'url' => '/uploads/images/' . $image['filename'],
-                'thumbnail_url' => '/uploads/images/' . $image['filename'],
+                'thumbnail_url' => $thumbnailUrl,
                 'uploaded_at' => $image['uploaded_at']
             ];
         }

+ 116 - 0
css/wysiwyg.css

@@ -581,3 +581,119 @@
         bottom: -20px;
     }
 }
+
+/* Gallery Delete Button */
+.gallery-item-container {
+    position: relative;
+}
+
+.gallery-delete-btn {
+    position: absolute;
+    top: 5px;
+    right: 5px;
+    width: 24px;
+    height: 24px;
+    background: rgba(220, 53, 69, 0.9);
+    color: white;
+    border: none;
+    border-radius: 50%;
+    font-size: 16px;
+    font-weight: bold;
+    cursor: pointer;
+    display: none;
+    align-items: center;
+    justify-content: center;
+    z-index: 10;
+    transition: all 0.2s ease;
+}
+
+.gallery-item:hover .gallery-delete-btn {
+    display: flex;
+}
+
+.gallery-delete-btn:hover {
+    background: rgba(220, 53, 69, 1);
+    transform: scale(1.1);
+}
+
+/* Notifications */
+.notification {
+    position: fixed;
+    top: 20px;
+    right: 20px;
+    padding: 12px 20px;
+    border-radius: 4px;
+    color: white;
+    font-weight: 500;
+    z-index: 10000;
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+    animation: slideIn 0.3s ease;
+}
+
+.notification-success {
+    background: #28a745;
+}
+
+.notification-error {
+    background: #dc3545;
+}
+
+.notification-info {
+    background: #17a2b8;
+}
+
+@keyframes slideIn {
+    from {
+        transform: translateX(100%);
+        opacity: 0;
+    }
+    to {
+        transform: translateX(0);
+        opacity: 1;
+    }
+}
+
+/* Thumbnail Links in Publications */
+.thumbnail-link {
+    display: inline-block;
+    margin: 10px 5px;
+    text-decoration: none;
+    border: none !important;
+    background: none !important;
+    padding: 0 !important;
+}
+
+.thumbnail-link:hover {
+    text-decoration: none !important;
+    background: none !important;
+}
+
+.thumbnail-link:focus {
+    outline: 2px solid #007bff;
+    outline-offset: 2px;
+}
+
+.thumbnail-image {
+    max-width: 300px;
+    height: auto;
+    border-radius: 4px;
+    box-shadow: 0 2px 8px rgba(0,0,0,0.1);
+    transition: transform 0.2s ease, box-shadow 0.2s ease;
+    cursor: pointer;
+}
+
+.thumbnail-image:hover {
+    transform: scale(1.02);
+    box-shadow: 0 4px 16px rgba(0,0,0,0.15);
+}
+
+/* Publication content image styling */
+.publication-body .thumbnail-link {
+    display: inline-block;
+    margin: 15px 10px 15px 0;
+}
+
+.publication-body .thumbnail-image {
+    max-width: 100%;
+    height: auto;
+}

+ 5 - 0
database_migrations/add_thumbnail_column.sql

@@ -0,0 +1,5 @@
+-- Add thumbnail_filename column to images table
+ALTER TABLE images ADD COLUMN thumbnail_filename VARCHAR(255) NULL AFTER filename;
+
+-- Add index for thumbnail filename
+ALTER TABLE images ADD INDEX idx_thumbnail_filename (thumbnail_filename);

+ 103 - 31
js/wysiwyg.js

@@ -198,10 +198,13 @@ class WYSIWYGEditor {
             item.dataset.imageName = image.original_name;
             
             item.innerHTML = `
-                <img src="${image.thumbnail_url}" alt="${image.original_name}" class="gallery-image">
-                <div class="gallery-info">
-                    <div class="gallery-name">${image.original_name}</div>
-                    <div class="gallery-size">${this.formatFileSize(image.size)}</div>
+                <div class="gallery-item-container">
+                    <img src="${image.thumbnail_url}" alt="${image.original_name}" class="gallery-image">
+                    <button class="gallery-delete-btn" onclick="wysiwygEditor.deleteImage(${image.id}, event)" title="Delete image">×</button>
+                    <div class="gallery-info">
+                        <div class="gallery-name">${image.original_name}</div>
+                        <div class="gallery-size">${this.formatFileSize(image.size)}</div>
+                    </div>
                 </div>
             `;
             
@@ -229,35 +232,57 @@ class WYSIWYGEditor {
         insertBtn.disabled = this.selectedImages.length === 0;
     }
     
-    insertSelectedImage() {
+    insertSelectedImages() {
         if (this.selectedImages.length === 0) return;
         
-        const selectedItems = document.querySelectorAll('.gallery-item.selected');
-        const images = [];
-        
-        selectedItems.forEach(item => {
-            images.push({
-                url: item.dataset.imageUrl,
-                name: item.dataset.imageName
-            });
-        });
-        
-        // Insert images into editor
-        images.forEach(image => {
-            const img = document.createElement('img');
-            img.src = image.url;
-            img.alt = image.name;
-            img.style.maxWidth = '100%';
-            img.style.height = 'auto';
-            
-            // Insert at cursor position
-            const selection = window.getSelection();
-            if (selection.rangeCount > 0) {
-                const range = selection.getRangeAt(0);
-                range.insertNode(img);
-                range.collapse(false);
-            } else {
-                this.content.appendChild(img);
+        // Insert thumbnail links into content
+        this.selectedImages.forEach(imageId => {
+            const image = this.galleryImages.find(img => img.id == imageId);
+            if (image) {
+                // Create link wrapper
+                const link = document.createElement('a');
+                link.href = image.url;
+                link.target = '_blank';
+                link.rel = 'noopener noreferrer';
+                link.className = 'thumbnail-link';
+                
+                // Create thumbnail image
+                const img = document.createElement('img');
+                img.src = image.thumbnail_url;
+                img.alt = image.original_name;
+                img.className = 'thumbnail-image';
+                img.style.maxWidth = '300px';
+                img.style.height = 'auto';
+                img.style.borderRadius = '4px';
+                img.style.boxShadow = '0 2px 8px rgba(0,0,0,0.1)';
+                img.style.transition = 'transform 0.2s ease, box-shadow 0.2s ease';
+                
+                // Add hover effect
+                img.addEventListener('mouseenter', () => {
+                    img.style.transform = 'scale(1.02)';
+                    img.style.boxShadow = '0 4px 16px rgba(0,0,0,0.15)';
+                });
+                
+                img.addEventListener('mouseleave', () => {
+                    img.style.transform = 'scale(1)';
+                    img.style.boxShadow = '0 2px 8px rgba(0,0,0,0.1)';
+                });
+                
+                // Add click prevention to avoid WYSIWYG selection
+                img.addEventListener('click', (e) => e.preventDefault());
+                link.addEventListener('click', (e) => e.preventDefault());
+                
+                // Assemble the link with image
+                link.appendChild(img);
+                
+                // Insert at cursor or end of content
+                if (this.selection) {
+                    this.selection.deleteContents();
+                    this.selection.insertNode(link);
+                    this.selection.collapse(false);
+                } else {
+                    this.content.appendChild(link);
+                }
             }
         });
         
@@ -266,6 +291,53 @@ class WYSIWYGEditor {
         this.closeImageGallery();
     }
     
+    async deleteImage(imageId, event) {
+        event.stopPropagation(); // Prevent image selection
+        
+        if (!confirm('Are you sure you want to delete this image?')) {
+            return;
+        }
+        
+        try {
+            const response = await fetch(`upload_image.php?id=${imageId}`, {
+                method: 'DELETE'
+            });
+            
+            const result = await response.json();
+            
+            if (result.success) {
+                // Remove the image from gallery
+                const galleryItem = document.querySelector(`[data-image-id="${imageId}"]`);
+                if (galleryItem) {
+                    galleryItem.remove();
+                }
+                
+                // Show success message
+                this.showNotification('Image deleted successfully', 'success');
+            } else {
+                this.showNotification('Failed to delete image', 'error');
+            }
+        } catch (error) {
+            console.error('Error deleting image:', error);
+            this.showNotification('Error deleting image', 'error');
+        }
+    }
+    
+    showNotification(message, type = 'info') {
+        // Create notification element
+        const notification = document.createElement('div');
+        notification.className = `notification notification-${type}`;
+        notification.textContent = message;
+        
+        // Add to page
+        document.body.appendChild(notification);
+        
+        // Remove after 3 seconds
+        setTimeout(() => {
+            notification.remove();
+        }, 3000);
+    }
+    
     setupImageUpload() {
         const uploadArea = document.getElementById('uploadArea');
         const fileInput = document.getElementById('fileInput');