# textdocs/views.py from django.shortcuts import render, get_object_or_404, redirect from django.urls import reverse_lazy # NEW: Useful for delete success URL from django.http import HttpResponse from django.template.loader import render_to_string from weasyprint import HTML from django.contrib.auth.decorators import login_required from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt from django.core.files.storage import default_storage import os from django.conf import settings from .models import Document, DocumentVersion # Changed from Document from .forms import DocumentVersionForm, DocumentCreateForm # Changed from DocumentForm from .utils import convert_media_urls_to_file_paths # NEW: Utility function for media URL conversion @login_required def document_list(request): """Shows a list of all created documents.""" documents = Document.objects.filter(author=request.user).order_by('-created_at') return render(request, 'textdocs/document_list.html', {'documents': documents}) @login_required def document_detail(request, pk): """Shows the details of a single document.""" document = get_object_or_404(Document, pk=pk, author=request.user) return render(request, 'textdocs/document_detail.html', {'document': document}) @login_required def document_create(request): """ Handles the creation of a new parent Document and its first version. This is a two-form process now, but we'll combine them logically. """ if request.method == 'POST': title_form = DocumentCreateForm(request.POST) content_form = DocumentVersionForm(request.POST) if title_form.is_valid() and content_form.is_valid(): # Create the parent Document document = Document.objects.create( author=request.user, title=title_form.cleaned_data['title'] ) # Create the first version version = content_form.save(commit=False) version.document = document version.version_number = 1 version.save() return redirect(document.get_absolute_url()) else: title_form = DocumentCreateForm() content_form = DocumentVersionForm() return render(request, 'textdocs/document_form.html', { 'title_form': title_form, 'content_form': content_form }) @login_required def document_edit(request, pk): """ Handles creating a NEW version of an existing document. This replaces the old 'update' view. """ document = get_object_or_404(Document, pk=pk, author=request.user) latest_version = document.latest_version if request.method == 'POST': form = DocumentVersionForm(request.POST) if form.is_valid(): new_version = form.save(commit=False) new_version.document = document # Increment the version number new_version.version_number = latest_version.version_number + 1 new_version.save() return redirect(document.get_absolute_url()) else: # Pre-populate the form with the content of the latest version form = DocumentVersionForm(instance=latest_version) return render(request, 'textdocs/document_edit_form.html', { 'form': form, 'document': document }) # NEW VIEW: To see old versions @login_required def view_version(request, pk, version_number): document = get_object_or_404(Document, pk=pk, author=request.user) version = get_object_or_404(DocumentVersion, document=document, version_number=version_number) return render(request, 'textdocs/version_detail.html', {'version': version}) # --- NEW: View for deleting a document --- @login_required def document_delete(request, pk): """Handles the deletion of a document and all its versions.""" document = get_object_or_404(Document, pk=pk, author=request.user) if request.method == 'POST': # This is after the user confirms the deletion document.versions.all().delete() # First delete all versions document.delete() # Then delete the document itself return redirect('document_list') # Redirect to the list after deletion # If it's a GET request, show the confirmation page return render(request, 'textdocs/document_confirm_delete.html', {'document': document}) @login_required def generate_pdf(request, pk): """ Generates a PDF for a document's LATEST version. This is triggered from the main document detail page. """ # First, get the parent document to ensure ownership document = get_object_or_404(Document, pk=pk, author=request.user) # MODIFIED: Get the latest version using our model property latest_version = document.latest_version # ADDED: A safety check in case a document has no versions if not latest_version: return HttpResponse("This document has no versions and cannot be exported.", status=404) # REUSE: We can use our existing version_pdf.html template! html_string = render_to_string('textdocs/version_pdf.html', {'version': latest_version}) html_string = convert_media_urls_to_file_paths(html_string) # NEW: Convert media URLs to file paths for WeasyPrint # Generate the PDF with WeasyPrint html = HTML(string=html_string) pdf = html.write_pdf() # Create the HTTP response with the PDF file response = HttpResponse(pdf, content_type='application/pdf') # Make the filename include the version number for clarity filename = f'{document.title}_v{latest_version.version_number}.pdf' response['Content-Disposition'] = f'attachment; filename="{filename}"' return response @login_required def generate_version_pdf(request, pk, version_number): """Generates a PDF for a SPECIFIC historical version of a document.""" # First, get the parent document to ensure ownership document = get_object_or_404(Document, pk=pk, author=request.user) # Now, get the specific version associated with that document version = get_object_or_404(DocumentVersion, document=document, version_number=version_number) # We'll create a dedicated PDF template for versions to keep things clean html_string = render_to_string('textdocs/version_pdf.html', {'version': version}) html_string = convert_media_urls_to_file_paths(html_string) # Convert media URLs to file paths for WeasyPrint # Generate the PDF with WeasyPrint html = HTML(string=html_string) pdf = html.write_pdf() # Create the HTTP response with the PDF file response = HttpResponse(pdf, content_type='application/pdf') # Make the filename include the version number for clarity filename = f'{version.document.title}_v{version.version_number}.pdf' response['Content-Disposition'] = f'attachment; filename="{filename}"' return response @csrf_exempt # Use with caution, see note below @login_required def tinymce_image_upload(request): """ Handles image uploads from the TinyMCE editor. """ if request.method != 'POST': return JsonResponse({'error': 'Invalid request method.'}) # 'file' is the name TinyMCE uses for the uploaded file file = request.FILES.get('file') if not file: return JsonResponse({'error': 'No file provided.'}) # You can add more validation here (e.g., file type, size) # Save the file to your media directory file_path = default_storage.save(os.path.join('tinymce_uploads', file.name), file) # Get the URL for the saved file file_url = default_storage.url(file_path) # Return the URL in the format TinyMCE expects return JsonResponse({'location': file_url}) @login_required def image_browser(request): """ Displays a gallery of all previously uploaded images. TinyMCE opens this page in a dialog, and clicking an image selects it and closes the dialog. """ upload_dir = os.path.join(settings.MEDIA_ROOT, 'tinymce_uploads') images = [] if os.path.exists(upload_dir): for filename in sorted(os.listdir(upload_dir)): # Only include common image file types if filename.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg')): images.append({ 'filename': filename, 'url': f'{settings.MEDIA_URL}tinymce_uploads/{filename}', }) return render(request, 'textdocs/image_browser.html', {'images': images})