| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- # 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})
|