diff --git a/README.md b/README.md index fc3095e..01128d8 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,135 @@ -# Pandoc Plugin -This is a plugin for [Janeway](https://github.com/BirkbeckCTP/janeway) that provides a button for typesetters to automatically generate html files from user article submissions in docx/rtf. These files are first converted to markdown, and from there to html, and then registered as galleys of the original article. +# Pandoc Plugin for Janeway -## How to install: +This plugin integrates [Pandoc](https://pandoc.org/) into the Janeway platform, enabling document conversion for articles. It supports generating HTML and PDF files from manuscripts and adding optional stamped coversheets. -(You can find plugin installation instructions in the README for the back_content plugin [here](https://github.com/BirkbeckCTP/back_content)) +## Features -1. SSH into the server and navigate to: /path/to/janeway/src/plugins -2. Use git to clone the plugin's repository here. For example: `git clone https://github.com/hackman104/pandoc_plugin.git` -3. Make sure you have activated janeway's virtual environment -4. Return to /path/to/janeway/src and run `python manage.py install_plugins` -5. Restart apache (command will depend on your distro) -6. Go to your journal webpage, go to the manager, and click "Plugins" at the bottom of the side-bar on the left -7. Find the plugin you are working on, click its link, and then enable it and click submit +- Converts Word documents (`.docx`) and RTF manuscripts into Markdown, HTML, and optionally PDF format. +- Automatically registers the generated HTML as galleys for the original article. +- Optional inclusion of custom coversheets for PDF generation. -### pandoc +## How to Install -*N. B. You must have pandoc installed on your server to use this plugin. Please see pandoc's installation documentation __[here](https://pandoc.org/installing.html)__.* +1. SSH into the server and navigate to the plugins directory: + ```bash + cd /path/to/janeway/src/plugins + ``` -Most of the package managers for Linux distributions offer older versions of Pandoc, and you need at least 1.13 for full docx support. Luckily, pandoc offers a compiled distribution in .deb format: +2. Use git to clone the plugin repository: + ```bash + git clone https://github.com/openlibhums/pandoc_plugin.git + ``` -``` sh +3. Activate Janeway's virtual environment: + ```bash + source /path/to/janeway/venv/bin/activate + ``` + +4. Return to the Janeway source directory and install the plugin: + ```bash + cd /path/to/janeway/src + pip3 install -r plugins/pandoc_plugin/requirements.txt + python manage.py install_plugins pandoc_plugin + ``` + +5. Restart your webserver to apply the changes (command depends on your distro). + +6. Log in to your journal's admin interface: + - Go to the "Manager" section. + - Click "Plugins" at the bottom of the left-hand sidebar. + - Locate the Pandoc Plugin, enable it, and click submit. + +### Installing Pandoc and XeLaTeX + +#### Pandoc + +You must have Pandoc installed on your server to use this plugin. Most Linux distributions include older versions of Pandoc, but at least version 1.13 is required for full `.docx` support. + +To install a newer version of Pandoc: + +```bash wget 'https://github.com/jgm/pandoc/releases/download/2.19.2/pandoc-2.19.2-1-amd64.deb' -dpkg -i pandoc-2.5-1-amd64.deb -rm pandoc-2.5-1-amd64.deb +dpkg -i pandoc-2.19.2-1-amd64.deb +rm pandoc-2.19.2-1-amd64.deb ``` -Pandoc should now be available for all users to run, ensuring the plugin will work + +Verify that Pandoc is installed and available: +```bash +pandoc --version +``` + +#### XeLaTeX + +The plugin uses `xelatex` as the PDF engine. If you encounter the error `xelatex not found`, follow these steps to install `xelatex` on your system. + +##### On Ubuntu/Debian: +To install `xelatex` using the TeX Live distribution: +```bash +sudo apt update +sudo apt install texlive-xetex +``` + +##### On CentOS/RHEL: +```bash +sudo yum install texlive-xetex +``` + +##### On macOS: +```bash +brew install --cask mactex +``` + +Verify `xelatex` is installed: +```bash +xelatex --version +``` + +## Configuration + +### Settings + +The plugin provides the following configurable settings: + +1. **Coversheet HTML (`cover_sheet`)**: + - Specifies the HTML template used for the stamped coversheet. + - Configured via the plugin manager interface. + +2. **Extract Images**: + - Boolean setting to enable extraction of images. + - Configured via the plugin manager interface. + + +### Updating Settings + +1. Go to the Janeway admin panel. +2. Navigate to `Settings > Plugins > Pandoc Plugin`. +3. Configure the above settings as needed. + +## Usage + +### Generating Files via Management Command + +To generate HTML or PDFs for specific articles or files, use the management command: + +```bash +python manage.py generate_pdfs --owner --conversion_type stamped or unstamped +``` + +- `article_id`: ID of the article to process. +- `--owner`: Email of the account owner for the generated file. +- `--conversion_type`: Tells Janeway whether to add a cover sheet to the converted file. + +#### Example Command: + +```bash +python manage.py generate_pdfs 123 456 --owner=editor@example.com +``` + +### File Generation in the User Interface + +- Editors can trigger HTML and PDF generation from the Janeway interface for individual files in typesetting by clicking the "Options" link + +## License + +This plugin is licensed under the [GNU Affero General Public License](https://www.gnu.org/licenses/agpl-3.0.en.html) (AGPLv3). diff --git a/convert.py b/convert.py index 5e643c0..3e31dab 100644 --- a/convert.py +++ b/convert.py @@ -3,11 +3,13 @@ import subprocess import tempfile +from django.conf import settings + from bs4 import BeautifulSoup +from PyPDF2 import PdfMerger + from core.files import IMAGE_MIMETYPES -from utils import models, setting_handler from utils.logger import get_logger - from plugins.pandoc_plugin import plugin_settings logger = get_logger(__name__) @@ -72,8 +74,143 @@ def generate_html_from_doc(doc_path, extract_images=False): return str(pandoc_soup), image_paths +def generate_pdf_with_cover_sheet(doc_path, mime_type, cover_sheet_html): + if mime_type not in plugin_settings.PDF_CONVERSION_SUPPORTED_MIME_TYPES: + raise TypeError(f"File MIME type {mime_type} not supported") + + images_temp_path = tempfile.mkdtemp() + cover_pdf_path = os.path.join(tempfile.gettempdir(), 'cover_sheet.pdf') + document_pdf_path = os.path.join(tempfile.gettempdir(), + f'{os.path.basename(doc_path)}_document.pdf') + merged_pdf_path = os.path.join(tempfile.gettempdir(), + f'{os.path.basename(doc_path)}_merged.pdf') + + try: + subprocess.run( + [ + 'pandoc', + '-o', + cover_pdf_path, + '--from=html', + '--to=pdf', + '--pdf-engine=xelatex', + ], + input=cover_sheet_html.encode(), + check=True, + ) + except subprocess.CalledProcessError as err: + raise PandocError( + f"Error during cover sheet PDF conversion: {str(err)}") + + pandoc_command = ( + PANDOC_CMD + + MEMORY_LIMIT_ARG + + [EXTRACT_MEDIA, images_temp_path] + + [doc_path, '-o', document_pdf_path, '--pdf-engine=xelatex'] + ) + + try: + logger.info(f"[PANDOC] Running command: {pandoc_command}") + subprocess.run(pandoc_command, check=True) + except subprocess.CalledProcessError as e: + raise PandocError(f"PandocError: {e.stderr}") + + image_paths = [ + os.path.join(base, f) + for base, _, files in os.walk(images_temp_path) + for f in files + if mimetypes.guess_type(f)[0] in IMAGE_MIMETYPES + ] + + merger = PdfMerger() + merger.append(cover_pdf_path) + merger.append(document_pdf_path) + with open(merged_pdf_path, 'wb') as merged_file: + merger.write(merged_file) + + return merged_pdf_path, image_paths + + +def convert_word_to_pdf(doc_path, output_filename): + """Convert Word doc (docx, rtf, odt, etc.) to PDF using Pandoc with MIME type validation.""" + mime_type, _ = mimetypes.guess_type(doc_path) + + if mime_type not in plugin_settings.PDF_CONVERSION_SUPPORTED_MIME_TYPES: + raise TypeError(f"Unsupported file type for conversion: {mime_type}") + + output_pdf_path = os.path.join(settings.BASE_DIR, 'files', 'temp', + output_filename) + + pandoc_command = [ + 'pandoc', + doc_path, + '-o', + output_pdf_path, + '--pdf-engine=xelatex', + '-V', + 'geometry:margin=1.5in', + ] + + try: + subprocess.run(pandoc_command, check=True) + except subprocess.CalledProcessError as e: + raise Exception(f"Error converting Word document to PDF: {e}") + + return output_pdf_path + + +def convert_html_to_pdf(html_content, output_filename): + """Convert HTML content to PDF using Pandoc.""" + output_pdf_path = os.path.join( + settings.BASE_DIR, + 'files', + 'temp', + output_filename, + ) + + try: + subprocess.run( + [ + 'pandoc', + '--from=html', + '--to=pdf', + '-o', + output_pdf_path, + '--pdf-engine=xelatex', + "-V", + "pagestyle=empty", + "-V", + "geometry:margin=1.5in", + ], + input=html_content.encode(), + check=True, + ) + except subprocess.CalledProcessError as e: + raise Exception(f"Error converting HTML to PDF: {e}") + + return output_pdf_path + + +def merge_pdfs(cover_pdf, doc_pdf, output_filename): + """Merge two PDFs (cover sheet and document) into one.""" + output_pdf_path = os.path.join(settings.BASE_DIR, 'files', 'temp', output_filename) + + merger = PdfMerger() + + try: + merger.append(cover_pdf) + merger.append(doc_pdf) + with open(output_pdf_path, 'wb') as merged_pdf: + merger.write(merged_pdf) + except Exception as e: + raise Exception(f"Error merging PDFs: {e}") + finally: + merger.close() + + return output_pdf_path + + class PandocError(Exception): def __init__(self, msg, cmd=None): super().__init__(self, msg) self.cmd = cmd - diff --git a/forms.py b/forms.py index e9fc4f0..b702620 100644 --- a/forms.py +++ b/forms.py @@ -1,5 +1,58 @@ from django import forms +from tinymce.widgets import TinyMCE + +from utils import setting_handler, models +from plugins.pandoc_plugin import plugin_settings + + class PandocAdminForm(forms.Form): - pandoc_enabled = forms.BooleanField(required=False) - pandoc_extract_images = forms.BooleanField(required=False) + pandoc_enabled = forms.BooleanField(label="Enable Pandoc", required=False) + pandoc_extract_images = forms.BooleanField(label="Extract Images", required=False) + cover_sheet = forms.CharField( + label="Cover Sheet", + widget=TinyMCE(attrs={'cols': 80, 'rows': 30}), + required=False + ) + def __init__(self, *args, journal=None, **kwargs): + """Initialize form with current plugin settings and apply help text.""" + super().__init__(*args, **kwargs) + self.journal = journal + self.plugin = models.Plugin.objects.get( + name=plugin_settings.SHORT_NAME, + ) + + # Initialize fields with settings values and help texts + pandoc_enabled_setting = setting_handler.get_plugin_setting( + self.plugin, 'pandoc_enabled', self.journal, create=True, + pretty='Enable Pandoc', types='boolean' + ) + self.fields[ + 'pandoc_enabled' + ].initial = pandoc_enabled_setting.processed_value + + extract_images_setting = setting_handler.get_plugin_setting( + self.plugin, 'pandoc_extract_images', self.journal, create=True, + pretty='Pandoc extract images', types='boolean' + ) + self.fields[ + 'pandoc_extract_images' + ].initial = extract_images_setting.processed_value + + cover_sheet_setting = setting_handler.get_plugin_setting( + self.plugin, 'cover_sheet', self.journal, create=True, + pretty='Cover Sheet', types='text' + ) + self.fields[ + 'cover_sheet' + ].initial = cover_sheet_setting.processed_value + + def save(self): + """Save each setting in the cleaned data to the plugin settings.""" + for setting_name, setting_value in self.cleaned_data.items(): + setting_handler.save_plugin_setting( + plugin=self.plugin, + setting_name=setting_name, + value=setting_value, + journal=self.journal + ) diff --git a/hooks.py b/hooks.py index f9bcd68..e134ad1 100644 --- a/hooks.py +++ b/hooks.py @@ -11,11 +11,23 @@ def inject_pandoc(context): """ plugin = models.Plugin.objects.get(name=plugin_settings.SHORT_NAME) request = context.get('request') - pandoc_enabled = setting_handler.get_plugin_setting(plugin, 'pandoc_enabled', request.journal, create=True, - pretty='Pandoc Enabled', types='boolean') + pandoc_enabled = setting_handler.get_plugin_setting( + plugin, + 'pandoc_enabled', + request.journal, + create=True, + pretty='Pandoc Enabled', + types='boolean', + ) if pandoc_enabled.processed_value: - return render_to_string('pandoc_plugin/inject.html', context={'article': context.get('article'), 'file': context.get('file')}, request=request) + return render_to_string( + 'pandoc_plugin/inject.html', + context={ + 'article': context.get('article'), + 'file': context.get('file')}, + request=request, + ) else: return '' @@ -26,8 +38,14 @@ def conversion_row_hook(context, file_, article): plugin = models.Plugin.objects.get(name=plugin_settings.SHORT_NAME) request = context["request"] - pandoc_enabled = setting_handler.get_plugin_setting(plugin, 'pandoc_enabled', request.journal, create=True, - pretty='Pandoc Enabled', types='boolean') + pandoc_enabled = setting_handler.get_plugin_setting( + plugin, + 'pandoc_enabled', + request.journal, + create=True, + pretty='Pandoc Enabled', + types='boolean', + ) if pandoc_enabled.processed_value: context={'article': article, 'file': file_} diff --git a/install/settings.json b/install/settings.json index ae35ea5..d2968ad 100644 --- a/install/settings.json +++ b/install/settings.json @@ -28,5 +28,20 @@ "value": { "default": "" } + }, + { + "group": { + "name": "plugin:pandoc_plugin" + }, + "setting": { + "description": "A rich text field for adding custom cover sheet content to PDF files.", + "is_translatable": false, + "name": "cover_sheet", + "pretty_name": "Pandoc Cover Sheet", + "type": "text" + }, + "value": { + "default": "" + } } ] \ No newline at end of file diff --git a/logic.py b/logic.py new file mode 100644 index 0000000..f69a44d --- /dev/null +++ b/logic.py @@ -0,0 +1,101 @@ +import os + +from django.shortcuts import get_object_or_404 +from django.template import Template, Context +from django.template.loader import render_to_string +from django.conf import settings + +from plugins.pandoc_plugin import plugin_settings, convert +from utils import setting_handler +from submission.models import Article +from core.models import File + + +def generate_pdf_with_cover_sheet( + article_id, + file_id, + journal=None, + temp_dir=None, + conversion_type="stamped", +): + # Fetch article and manuscript + article = get_object_or_404(Article, pk=article_id) + manuscript = get_object_or_404(File, pk=file_id, article_id=article.pk) + + # Fetch plugin settings + plugin = plugin_settings.PandocPlugin.get_self() + + # Set up temporary directory + if temp_dir is None: + temp_dir = os.path.join(settings.BASE_DIR, "files", "temp") + if not os.path.exists(temp_dir): + os.makedirs(temp_dir) + + doc_pdf_path = cover_pdf_path = merged_pdf_path = None + + try: + # Convert the Word document to PDF + file_path = manuscript.self_article_path() + doc_pdf_path = convert.convert_word_to_pdf( + file_path, + f"{manuscript.pk}_document.pdf", + ) + + if conversion_type == "stamped": + # Fetch and render the cover sheet HTML + cover_sheet_html = setting_handler.get_plugin_setting( + plugin=plugin, + setting_name="cover_sheet", + journal=journal or article.journal, + create=True, + ).processed_value + context = Context({"article": article}) + rendered_html = Template(cover_sheet_html).render(context) + + # Convert the HTML cover sheet to PDF + cover_pdf_path = convert.convert_html_to_pdf( + rendered_html, + f"{manuscript.pk}_cover.pdf", + ) + + # Merge the PDFs + merged_pdf_path = convert.merge_pdfs( + cover_pdf_path, + doc_pdf_path, + f"{manuscript.pk}_merged.pdf", + ) + + # Set the final PDF path + final_pdf_path = merged_pdf_path + else: + # If no cover sheet, the final PDF is the converted Word document + final_pdf_path = doc_pdf_path + + # Ensure the target directory exists + target_directory = os.path.join( + settings.BASE_DIR, + "files", + "articles", + str(article.pk), + ) + if not os.path.exists(target_directory): + os.makedirs(target_directory) + + # Set the target path for the final PDF + final_target_path = os.path.join( + target_directory, + os.path.basename(final_pdf_path), + ) + + # Move the final PDF to the correct directory if necessary + if final_pdf_path != final_target_path: + os.rename(final_pdf_path, final_target_path) + + return final_target_path, article + + finally: + # Clean up temp files if they exist + for temp_file in [doc_pdf_path, cover_pdf_path, merged_pdf_path]: + if temp_file and os.path.exists(temp_file): + os.remove(temp_file) + diff --git a/management/__init__.py b/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/management/commands/__init__.py b/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/management/commands/generate_pdf_with_cover_sheet.py b/management/commands/generate_pdf_with_cover_sheet.py new file mode 100644 index 0000000..2588f96 --- /dev/null +++ b/management/commands/generate_pdf_with_cover_sheet.py @@ -0,0 +1,158 @@ +from django.core.management.base import BaseCommand + +from submission.models import Article +from core.models import File, Account +from plugins.pandoc_plugin import logic +from production import logic as production_logic +from utils.testing.helpers import Request + + +class Command(BaseCommand): + help = "Generate PDFs with cover sheets for selected files of specified articles." + + def add_arguments(self, parser): + parser.add_argument( + "article_ids", + nargs="+", + type=int, + help=( + "IDs of the articles to process. " + "Multiple IDs can be provided." + ), + ) + parser.add_argument( + "--owner", + type=str, + required=True, + help="Email or username of the account owner for the file.", + ) + parser.add_argument( + "--conversion_type", + type=str, + required=True, + help="Either stamped or unstamped", + ) + + def handle(self, *args, **options): + article_ids = options["article_ids"] + conversion_type = options.get("conversion_type", "stamped") + owner_identifier = options["owner"] + + try: + owner = Account.objects.get( + email=owner_identifier, + ) + except Account.DoesNotExist: + try: + owner = Account.objects.get(username=owner_identifier) + except Account.DoesNotExist: + self.stderr.write( + self.style.ERROR( + f"Account with email or username '{owner_identifier}' does not exist." + ) + ) + return + + for article_id in article_ids: + try: + article = Article.objects.get(pk=article_id) + self.stdout.write( + self.style.SUCCESS( + f"Processing Article ID: {article_id} - {article.title}" + ) + ) + + files = File.objects.filter( + article_id=article.pk, + is_galley=False, + ) + if not files.exists(): + self.stdout.write( + self.style.WARNING( + f"No files found for Article ID: {article_id}" + ) + ) + continue + + self.stdout.write("Available files:") + file_choices = { + str(i): file + for i, file in enumerate(files, start=1) + } + for index, file in file_choices.items(): + self.stdout.write( + f"{index}: {file.label} (MIME: {file.mime_type})" + ) + + selected_indices = input( + "Enter the numbers of the files to process, " + "separated by commas (or press Enter to skip): " + ) + if not selected_indices.strip(): + self.stdout.write( + self.style.WARNING( + f"Skipping Article ID: {article_id}." + ) + ) + continue + + selected_files = [ + file_choices[index.strip()] + for index in selected_indices.split(",") + if index.strip() in file_choices + ] + + if not selected_files: + self.stdout.write( + self.style.WARNING( + f"No valid selections made for Article ID: {article_id}." + ) + ) + continue + + for file in selected_files: + try: + final_pdf_path, article = logic.generate_pdf_with_cover_sheet( + article_id=article.pk, + file_id=file.pk, + conversion_type=conversion_type, + ) + request = Request() + request.user = owner + production_logic.save_galley( + article=article, + request=request, + uploaded_file=final_pdf_path, + is_galley=True, + label="PDF", + save_to_disk=False, + public=True, + ) + self.stdout.write( + self.style.SUCCESS( + f"Generated PDF for Article ID: {article.pk}, " + f"File ID: {file.pk}. " + f"Saved at {final_pdf_path}." + ) + ) + except Exception as e: + self.stderr.write( + self.style.ERROR( + f"Error processing File ID: {file.pk}. " + f"Error: {str(e)}" + ) + ) + + except Article.DoesNotExist: + self.stderr.write( + self.style.ERROR( + f"Article with ID {article_id} does not exist." + ) + ) + except Exception as e: + self.stderr.write( + self.style.ERROR( + f"Error processing Article ID: {article_id}. " + f"Error: {str(e)}" + ) + ) diff --git a/plugin_settings.py b/plugin_settings.py index 127470a..bfe5d62 100644 --- a/plugin_settings.py +++ b/plugin_settings.py @@ -10,6 +10,12 @@ MANAGER_URL = 'pandoc_index' MEMORY_LIMIT_MB = 512 +PDF_CONVERSION_SUPPORTED_MIME_TYPES = [ + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'application/rtf', + 'application/vnd.oasis.opendocument.text', + 'text/html', +] class PandocPlugin(plugins.Plugin): diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..40badc7 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +PyPDF2>=3.0.0 \ No newline at end of file diff --git a/templates/pandoc_plugin/inject.html b/templates/pandoc_plugin/inject.html index a642fc9..4b26f30 100644 --- a/templates/pandoc_plugin/inject.html +++ b/templates/pandoc_plugin/inject.html @@ -1,6 +1,10 @@
-
- {% csrf_token %} - -
+
+ {% csrf_token %} + +
+
+ {% csrf_token %} + +
diff --git a/templates/pandoc_plugin/row_hook.html b/templates/pandoc_plugin/row_hook.html index 0fdaac3..b2eb424 100644 --- a/templates/pandoc_plugin/row_hook.html +++ b/templates/pandoc_plugin/row_hook.html @@ -1,10 +1,15 @@ Pandoc (Plugin) - Pandoc transforms a .docx or .rtf file into an HTML document that can be rendered on the browser + Pandoc transforms a .docx, .odt, .rtf, or .html file into an HTML or PDF document -
- {% csrf_token %} - -
+
+ {% csrf_token %} + +
+
+ {% csrf_token %} + + +
- + \ No newline at end of file diff --git a/urls.py b/urls.py index 85b6a69..170188f 100644 --- a/urls.py +++ b/urls.py @@ -3,6 +3,19 @@ from plugins.pandoc_plugin import views urlpatterns = [ - re_path(r'^$', views.index, name='pandoc_index'), - re_path(r'^convert/(?P\d+)/file/(?P\d+)/$', views.convert_file, name='pandoc_convert'), + re_path( + r'^$', + views.index, + name='pandoc_index', + ), + re_path( + r'^convert/(?P\d+)/file/(?P\d+)/$', + views.convert_file, + name='pandoc_convert', + ), + re_path( + r'^convert-to-pdf/(?P\d+)/(?P\d+)/$', + views.convert_to_pdf, + name='convert_to_pdf', + ), ] diff --git a/views.py b/views.py index 89889e1..90b722a 100644 --- a/views.py +++ b/views.py @@ -3,51 +3,42 @@ from django.shortcuts import redirect, render, get_object_or_404 from django.contrib import messages from django.core.files.base import ContentFile -from django.urls import reverse from django.http import HttpResponseRedirect from django.views.decorators.http import require_POST from core import models as core_models -from production import logic +from production import logic as production_logic from security.decorators import production_user_or_editor_required from submission import models as sub_models from utils import setting_handler, models -from plugins.pandoc_plugin import forms, plugin_settings, convert +from plugins.pandoc_plugin import forms, plugin_settings, convert, logic def index(request): - """ - Render admin page allowing users to enable or disable the plugin - """ - plugin = models.Plugin.objects.get(name=plugin_settings.SHORT_NAME) - pandoc_enabled = setting_handler.get_plugin_setting(plugin, 'pandoc_enabled', request.journal, create=True, - pretty='Enable Pandoc', types='boolean').processed_value - extract_images = setting_handler.get_plugin_setting( - plugin, 'pandoc_extract_images', request.journal, create=True, - pretty='Pandoc extract images', types='boolean').processed_value - - admin_form = forms.PandocAdminForm(initial={ - 'pandoc_enabled': pandoc_enabled, - 'pandoc_extract_images': extract_images, - }) - - if request.POST: - admin_form = forms.PandocAdminForm(request.POST) - - if admin_form.is_valid(): - for setting_name, setting_value in admin_form.cleaned_data.items(): - setting_handler.save_plugin_setting(plugin, setting_name, setting_value, request.journal) - messages.add_message(request, messages.SUCCESS, '{0} setting updated.'.format(setting_name)) - - return redirect(reverse('pandoc_index')) - - template = "pandoc_plugin/index.html" - context = { - 'admin_form': admin_form, - } + """Render admin page for configuring the Pandoc plugin.""" + form = forms.PandocAdminForm( + journal=request.journal, + ) - return render(request, template, context) + if request.method == 'POST': + form = forms.PandocAdminForm( + request.POST, + journal=request.journal, + ) + if form.is_valid(): + form.save() + messages.success( + request, + 'Settings updated successfully', + ) + return redirect('pandoc_index') + + return render( + request, + 'pandoc_plugin/index.html', + {'admin_form': form}, + ) @require_POST @@ -81,7 +72,7 @@ def convert_file(request, article_id=None, file_id=None): with open(output_path, mode="w", encoding="utf-8") as html_file: print(html, file=html_file) - galley = logic.save_galley( + galley = production_logic.save_galley( article, request, output_path, @@ -96,9 +87,57 @@ def convert_file(request, article_id=None, file_id=None): with open(image, 'rb') as image_reader: image_file = ContentFile(image_reader.read()) image_file.name = image_name - logic.save_galley_image(galley, request, image_file, image_name, - fixed=False) + production_logic.save_galley_image( + galley, + request, + image_file, + image_name, + fixed=False, + ) return HttpResponseRedirect(request.META.get('HTTP_REFERER')) -# NEED LOGIC FOR IF HTML ALREADY GENERATED + +@require_POST +@production_user_or_editor_required +def convert_to_pdf(request, article_id, file_id): + conversion_type = request.POST.get('convert_pdf') + print(request.POST) + if conversion_type and conversion_type in ['stamped', 'unstamped']: + try: + # Generate the PDF with a cover sheet + final_pdf_path, article = logic.generate_pdf_with_cover_sheet( + article_id=article_id, + file_id=file_id, + journal=request.journal, + conversion_type=conversion_type + ) + + # Save the merged PDF as a galley + production_logic.save_galley( + article=article, + request=request, + uploaded_file=final_pdf_path, + is_galley=True, + label="PDF", + save_to_disk=False, + public=True, + ) + + if conversion_type == "stamped": + message = "PDF with cover sheet generated successfully." + else: + message = "PDF without cover sheet generated successfully" + messages.success( + request, + message, + ) + except Exception as e: + messages.error(request, f"Error during PDF generation: {str(e)}") + else: + messages.add_message( + request, + messages.WARNING, + 'Conversion type must be either stamped or unstamped.', + ) + return HttpResponseRedirect(request.META.get("HTTP_REFERER"))