Skip to content

Image Upload Functionality

Overview

This document explains how image uploads work in Arctyk ITSM comments and tickets, and how it compares to Jira's approach.

How Jira Handles Uploads

Images in Comments/Descriptions

  • Direct insertion: Users drag/drop, paste, or click "Insert Image" in comment editor
  • Hosted on Jira: Images are uploaded to Jira servers and stored with the issue
  • Markdown/Wiki syntax: References like ![image.jpg] or URL-based embed
  • Permissions: Only authorized users can upload; file types restricted (no executables, etc.)

Attachments (File Upload)

  • Separate from body: Files are attached independently from comment text
  • UI: File upload widget shows list of attachments, separate from rich text editor
  • Permissions: Attachment download requires read access to issue
  • Retention: Files stored indefinitely; can be downloaded or deleted by authorized users

Audit Trail

  • Upload logged with: User, Timestamp, File size, IP address
  • Appears in activity feed: "User uploaded filename.pdf"
  • Change history tracks attachment additions/removals

Arctyk Implementation

Phase 1: Image Upload (NOW IMPLEMENTED ✅)

Features:

  • ✅ Drag/drop images directly into TinyMCE editors (comments, descriptions, inline edits)
  • ✅ Paste images from clipboard
  • ✅ Click "Insert → Image" button in toolbar
  • ✅ Images stored in /media/uploads/comments/{YEAR}/ on server
  • ✅ File validation: .jpg, .jpeg, .png, .gif, .webp, .svg only
  • ✅ Size limit: 5MB per image (configurable in image_upload view)
  • ✅ Authentication required: Only logged-in users can upload
  • ✅ Automatic audit logging: ChangeLog captures comment creation with images

File Structure:

media/
  uploads/
    comments/
      2024/
        john_smith_20240104_143022_a1b2c3d4.png
        jane_doe_20240104_150315_e5f6g7h8.jpg

Configuration (in src/config/settings.py):

TINYMCE_DEFAULT_CONFIG = {
    ...
    "images_upload_url": "/tickets/image-upload/",
    "images_upload_credentials": True,
}

API Endpoint (in src/tickets/views.py):

  • POST /tickets/image-upload/ - Upload single image
  • Requires: Authentication (@login_required)
  • Content-Type: multipart/form-data with file field
  • Response: { "location": "/media/uploads/comments/2024/username_timestamp_uuid.jpg" }

TinyMCE Integration:

  • Images inserted via TinyMCE's "Insert Image" button or drag/drop
  • Uses /media/uploads/comments/{YEAR}/ URLs
  • Images are embedded as <img> tags in comment HTML

Phase 2: File Attachments (Future - Optional)

If Jira-like file attachment feature is desired:

Proposed Model:

class CommentAttachment(models.Model):
    comment = models.ForeignKey(Comment, on_delete=models.CASCADE, related_name='attachments')
    file = models.FileField(upload_to='attachments/comments/%Y/%m/')
    uploaded_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
    uploaded_at = models.DateTimeField(auto_now_add=True)
    file_size = models.BigIntegerField()
    file_type = models.CharField(max_length=20)  # .pdf, .docx, .zip, etc.

Benefits:

  • Separate from comment body text
  • Better for large files (PDFs, documents, spreadsheets)
  • File listing UI in comment details
  • Download/delete operations
  • Storage tracking

Usage

For End Users

In Comment Editor:

  1. Type your comment text
  2. To add an image, one of:
  3. Drag/drop image from file explorer into the editor
  4. Paste image from clipboard (Ctrl+V / Cmd+V)
  5. Click Insert → Image in toolbar, select image file
  6. Image automatically uploads and embeds in comment
  7. Submit comment - image is saved with the comment

In Ticket Description:

  • Same process: drag/drop, paste, or click toolbar button

In Inline Edit (Description):

  • Same process for quick edits

For Administrators

File Storage Location:

# On server
/code/media/uploads/comments/2024/

# Accessible via web
http://example.com/media/uploads/comments/2024/john_smith_20240104_143022_uuid.jpg

Cleanup (if needed):

# Remove old images (older than 30 days)
find /code/media/uploads/comments -type f -mtime +30 -delete

File Size Limit: Change in src/tickets/views.pyimage_upload() function:

MAX_FILE_SIZE = 5 * 1024 * 1024  # 5MB (change as needed)

Allowed File Types: Change in src/tickets/views.pyimage_upload() function:

ALLOWED_EXTENSIONS = {'.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg'}

Security Considerations

✅ Currently Implemented

  • Authentication: Only logged-in users can upload
  • File validation: Only image formats allowed
  • Size limit: 5MB max (prevents abuse)
  • File naming: User + timestamp + UUID (prevents overwrites)
  • No execution: Images stored in safe directory (not in CODE path)

⚠️ Future Improvements

  • Virus scanning: Integrate ClamAV or similar for malware detection
  • Rate limiting: Limit uploads per user/hour (prevent spam)
  • Quota: Per-user or per-ticket storage quota
  • CDN: Consider CloudFront or similar for large deployments

Comparison: Jira vs Arctyk

Feature Jira Arctyk (Current) Arctyk (Optional)
Image in Comments ✅ Yes ✅ Yes
Image in Descriptions ✅ Yes ✅ Yes
Drag/Drop ✅ Yes ✅ Yes
Paste from Clipboard ✅ Yes ✅ Yes
File Attachments ✅ Yes (separate) ❌ No ✅ (planned)
Attachment List UI ✅ Yes ❌ No ✅ (planned)
Download Files ✅ Yes N/A ✅ (planned)
Virus Scanning ✅ Yes (Enterprise) ❌ No ✅ (planned)
Audit Trail ✅ Yes (uploads logged) ✅ Yes (via ChangeLog) ✅ (with attachments)

Testing

Manual Test Steps

  1. Create/Edit Comment:

  2. Navigate to ticket detail page

  3. Scroll to "Add Comment" form
  4. Drag an image file into the editor
  5. Verify image appears inline
  6. Submit comment
  7. Verify image persists in saved comment

  8. Test Inline Edit:

  9. Click "Edit" on ticket description

  10. Drag image into editor
  11. Click save
  12. Verify image appears in description

  13. Test Edit Comment:

  14. Click "Edit" on existing comment

  15. Add another image to the comment
  16. Submit
  17. Verify both images are shown

  18. Size/Type Validation:

  19. Try uploading non-image file (.pdf, .txt) → should fail
  20. Try uploading very large image (>5MB) → should fail
  21. Try uploading .jpg, .png, .gif → should succeed

Automated Tests (TODO)

Consider adding test cases to src/tickets/tests/:

  • test_image_upload_success() - Valid image upload
  • test_image_upload_large_file() - Reject oversized file
  • test_image_upload_invalid_type() - Reject non-image
  • test_image_upload_unauthenticated() - Reject non-logged-in user

Production Deployment

Docker/Gunicorn Setup

For production, media files are served via:

Option 1: WhiteNoise (Current)

  • Already configured in STATIC_ROOT settings
  • Update Django to serve media via WhiteNoise (if needed)

Option 2: Nginx Reverse Proxy

location /media/ {
    alias /code/media/;
    expires 30d;
}

Option 3: S3/CloudFront (AWS)

# settings.py
if not DEBUG:
    AWS_STORAGE_BUCKET_NAME = "my-bucket"
    MEDIA_URL = f"https://{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/"
    DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"

Volumes/Persistence

Ensure Docker volume mounts media directory:

# docker-compose.yml
services:
  web:
    volumes:
      - ./media:/code/media

Future Enhancements

  1. Image Optimization: Auto-resize large images, create thumbnails
  2. Image Gallery: UI to browse/manage uploaded images
  3. File Attachments: Separate file upload field for non-image files
  4. Virus Scanning: ClamAV integration for malware detection
  5. Rate Limiting: Prevent upload spam
  6. Inline Previews: Show preview of uploaded images before submit
  7. Image Metadata: Extract and display EXIF data (if present)
  8. External Storage: S3, Azure Blob, Google Cloud Storage support

Changelog Integration

All image uploads are automatically logged in the ChangeLog system:

# When comment with image is created:
ChangeLog.objects.create(
    content_type=ContentType.objects.get_for_model(Comment),
    object_id=comment.id,
    changed_by=request.user,
    change_summary="Created comment with image",
    timestamp=timezone.now(),
    ip_address=get_client_ip(request),
)

This ensures:

  • ✅ Who uploaded the image (user)
  • ✅ When it was uploaded (timestamp)
  • ✅ From where (IP address)
  • ✅ What changed (comment with image content)

Questions?

For more details on image handling or to request file attachment support, contact the development team.