1
0

vault backup: 2025-12-26 02:09:22
All checks were successful
Deploy Quartz site to GitHub Pages / build (push) Successful in 2m29s

This commit is contained in:
2025-12-26 02:09:22 +01:00
parent 3fddadfe50
commit 50366b9b9c
288 changed files with 58893 additions and 750 deletions

View File

@@ -0,0 +1,18 @@
from .upload_files_page_view import upload_files_page
from .upload_files_api_view import upload_files_api
from .explorer_view import explorer_view, pdf_viewer_page, markdown_editor_page
from .tree_api_view import get_file_tree
from .content_api_view import get_file_content, save_file_content, serve_pdf_api
__all__ = [
'upload_files_page',
'upload_files_api',
'explorer_view',
'pdf_viewer_page',
'markdown_editor_page',
'get_file_tree',
'get_file_content',
'save_file_content',
'serve_pdf_api',
]

View File

@@ -0,0 +1,48 @@
import json
from django.http import JsonResponse
from django.views.decorators.http import require_http_methods
from django.shortcuts import get_object_or_404
from file.models import File
@require_http_methods(["GET"])
def get_file_content(request, file_id):
"""Get the text content of a specific file."""
file_obj = get_object_or_404(File, id=file_id, user=request.quiz_user)
return JsonResponse({
'id': file_obj.id,
'name': file_obj.name,
'content': file_obj.text
})
@require_http_methods(["POST"])
def save_file_content(request, file_id):
"""Save updated text content to a file."""
file_obj = get_object_or_404(File, id=file_id, user=request.quiz_user)
try:
data = json.loads(request.body)
new_content = data.get('content', '')
file_obj.text = new_content
file_obj.save()
return JsonResponse({'success': True})
except (json.JSONDecodeError, KeyError):
return JsonResponse({'success': False, 'error': 'Invalid data'}, status=400)
from django.http import FileResponse
def serve_pdf_api(request, file_id):
"""Serve the raw PDF file for viewing."""
file_obj = get_object_or_404(File, id=file_id, user=request.quiz_user)
if not file_obj.mime_type == 'application/pdf' or not file_obj.file_content:
return JsonResponse({'error': 'Not a PDF file'}, status=400)
response = FileResponse(
file_obj.file_content.open('rb'),
content_type='application/pdf'
)
# Add CORS headers to allow CDN-hosted PDF.js viewer to fetch the PDF
response['Access-Control-Allow-Origin'] = '*'
response['Access-Control-Allow-Methods'] = 'GET, OPTIONS'
response['Access-Control-Allow-Headers'] = 'Content-Type'
return response

View File

@@ -0,0 +1,32 @@
from django.shortcuts import render
def explorer_view(request):
"""Render the file explorer page."""
return render(request, 'file/explorer.html')
def pdf_viewer_page(request, file_id):
"""Render the PDF viewer template for a specific file."""
from django.urls import reverse
from file.models import File
file_obj = File.objects.get(id=file_id, user=request.quiz_user)
relative_url = reverse('file:serve_pdf', args=[file_id])
# Build absolute URL for PDF.js library
pdf_url = request.build_absolute_uri(relative_url)
return render(request, 'file/pdf_viewer.html', {
'pdf_url': pdf_url,
'file_name': file_obj.name
})
def markdown_editor_page(request, file_id):
"""Render the Markdown editor template for a specific file."""
from django.urls import reverse
from file.models import File
file_obj = File.objects.get(id=file_id, user=request.quiz_user)
context = {
'file_id': file_id,
'file_name': file_obj.name,
'get_content_url': reverse('file:get_content', args=[file_id]),
'save_content_url': reverse('file:save_content', args=[file_id]),
}
return render(request, 'file/markdown_editor.html', context)

View File

@@ -0,0 +1,31 @@
from django.http import JsonResponse
from django.views.decorators.http import require_http_methods
from file.models import File
@require_http_methods(["GET"])
def get_file_tree(request):
"""Return the hierarchical file tree for the user."""
files = File.objects.filter(user=request.quiz_user).select_related('parent').order_by('name')
# Create a mapping of id -> item
item_map = {}
for f in files:
item_map[f.id] = {
'id': f.id,
'name': f.name,
'path': f.path,
'type': 'folder' if f.mime_type == 'application/x-folder' else 'file',
'mime_type': f.mime_type,
'children': [],
'content': f.text if f.mime_type.startswith('text/') else None
}
root_items = []
for f in files:
item = item_map[f.id]
if f.parent_id:
if f.parent_id in item_map:
item_map[f.parent_id]['children'].append(item)
else:
root_items.append(item)
return JsonResponse(root_items, safe=False)

View File

@@ -0,0 +1,101 @@
import os
import mimetypes
from pathlib import Path
from django.http import JsonResponse
from django.views.decorators.http import require_http_methods
from file.models import File
@require_http_methods(["POST"])
def upload_files_api(request):
"""Handle file/folder uploads and create File model instances"""
uploaded_files = request.FILES.getlist('files')
if not uploaded_files:
return JsonResponse({'error': 'No files uploaded'}, status=400)
created_files = []
folder_cache = {} # Cache for created folder objects
for idx, uploaded_file in enumerate(uploaded_files):
# Get the relative path if it exists (from webkitRelativePath)
relative_path = request.POST.get(f'path_{idx}', '')
if relative_path:
# This is from a folder upload
path_obj = Path(relative_path)
parts = path_obj.parts
# Create parent folders if needed
parent = None
for i, part in enumerate(parts[:-1]): # Exclude the file itself
folder_path = os.path.join(*parts[:i+1])
if folder_path not in folder_cache:
# Create or get folder
folder, created = File.objects.get_or_create(
user=request.quiz_user,
path=folder_path,
defaults={
'name': part,
'mime_type': 'application/x-folder',
'parent': parent
}
)
folder_cache[folder_path] = folder
parent = folder_cache[folder_path]
file_path = relative_path
file_name = parts[-1]
else:
# Single file upload
file_path = uploaded_file.name
file_name = uploaded_file.name
parent = None
# Determine MIME type
mime_type, _ = mimetypes.guess_type(file_name)
if not mime_type:
mime_type = 'application/octet-stream'
# Read file content (for text files, store in text field)
text_content = ''
if mime_type.startswith('text/'):
try:
content_bytes = uploaded_file.read()
text_content = content_bytes.decode('utf-8')
uploaded_file.seek(0) # Reset for saving to disk
except (UnicodeDecodeError, AttributeError):
uploaded_file.seek(0)
# Generate unique filename with 8-digit hash
import hashlib
file_hash = hashlib.md5(f"{file_path}{uploaded_file.name}".encode()).hexdigest()[:8]
name_parts = os.path.splitext(file_name)
unique_filename = f"{name_parts[0]}_{file_hash}{name_parts[1]}"
# Create File instance
file_obj = File.objects.create(
user=request.quiz_user,
name=file_name,
path=file_path,
mime_type=mime_type,
parent=parent,
text=text_content
)
# Save the uploaded file to disk (not for folders)
if mime_type != 'application/x-folder':
file_obj.file_content.save(unique_filename, uploaded_file, save=True)
created_files.append(file_obj)
return JsonResponse({
'success': True,
'count': len(created_files),
'files': [{'name': f.name, 'path': f.path} for f in created_files]
})

View File

@@ -0,0 +1,7 @@
from django.shortcuts import render
def upload_files_page(request):
"""Render the file upload interface"""
return render(request, 'file/upload_files.html')