Skip to content
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
48450d4
modification des fichiers qui font la configuration des exporters
sadok-lajmi Nov 5, 2025
65bbc44
Adding the empty layouts in resources/resource/layout/academicpages
GreLucie Nov 5, 2025
a4e2fea
création du fichier academicpages.article.layout
sadok-lajmi Nov 5, 2025
3e34b92
utilisation du fichier article.layout pour le new TemplateExporter
sadok-lajmi Nov 5, 2025
8e76f57
generalisation du layout en utilisant academicpages.layout
sadok-lajmi Nov 5, 2025
2799f1d
améliorations au academicpages layout
sadok-lajmi Nov 6, 2025
be96c94
ajout d'un custom formatter to help with academic layout
sadok-lajmi Nov 25, 2025
1421e98
AcademicPagesExporter
GreLucie Nov 26, 2025
e4a987c
test: add AcademicPagesExporter tests
AloisHasNeurons Nov 28, 2025
df7aa05
Delete unecessary imports
GreLucie Nov 29, 2025
9b12666
Changelog
GreLucie Nov 29, 2025
f588da4
Changed CHANGELOG.md
GreLucie Nov 29, 2025
de65527
trying to fix the CHANGELOG.md
GreLucie Nov 29, 2025
2d8afcf
Adding blankspace in CHANGELOG.md
GreLucie Nov 29, 2025
4ea372d
Correction du nouveau exporter + Cohérence des noms des fichiers prod…
sadok-lajmi Nov 29, 2025
3972b34
Merge remote-tracking branch 'origin/fix-for-issue-12727' into fix-fo…
sadok-lajmi Nov 29, 2025
9b8a86d
Reverted the date on the CHANGELOG.md and one change on AcademicPages…
GreLucie Dec 1, 2025
9087390
revert all changes to Changelog.md
GreLucie Dec 1, 2025
655afe3
format correction for the file : LayoutEntry
sadok-lajmi Dec 2, 2025
4d7dac1
Merge remote-tracking branch 'origin/fix-for-issue-12727' into fix-fo…
sadok-lajmi Dec 2, 2025
30e7626
trying to fix Open Rewrite problem
GreLucie Dec 2, 2025
6e07a52
Refactor AcademicPagesExporterTest to use full content comparison
AloisHasNeurons Dec 2, 2025
01f43cc
Merge branch 'fix-for-issue-12727' of https://github.com/GreLucie/jab…
AloisHasNeurons Dec 2, 2025
ad339be
Removed false import statement in tests.
AloisHasNeurons Dec 2, 2025
aaa5855
deleted useless import in AcademicPagesExporter.java
GreLucie Dec 2, 2025
3547cd0
Merge branch 'fix-for-issue-12727' of https://github.com/GreLucie/jab…
GreLucie Dec 2, 2025
7b55f27
correction of the error given by the localizationconsistancy test
sadok-lajmi Dec 2, 2025
ec19bb9
Merge branch 'main' into fix-for-issue-12727
sadok-lajmi Dec 4, 2025
05792cb
Merge branch 'main' into fix-for-issue-12727
GreLucie Dec 5, 2025
7ccbc6f
Updated CHANGELOG.md
GreLucie Dec 5, 2025
6f25e5c
added the link to issue 12727
GreLucie Dec 5, 2025
3c24b32
Update jablib/src/main/java/org/jabref/logic/exporter/AcademicPagesEx…
GreLucie Dec 6, 2025
b38bed2
Adressing some of the comments regarding the PR
GreLucie Dec 6, 2025
fe03fc4
Merge branch 'main' into fix-for-issue-12727
GreLucie Dec 6, 2025
c2a06e0
Comment added for replaceFormatter.setArgument(' ,-')
GreLucie Dec 6, 2025
280a1fb
tests: added 4 test cases and specified the SaveException
AloisHasNeurons Dec 6, 2025
6a4c5b0
Fixed submodules?
AloisHasNeurons Dec 6, 2025
100fe95
add all formatters as class variable
Memeel Dec 6, 2025
07088a8
changed a comment
GreLucie Dec 6, 2025
fcd3db4
Trying to fix indentation in AcademicPagesExporterTest
GreLucie Dec 6, 2025
1efe4a2
Revert change on AcademicPagesExporter
GreLucie Dec 6, 2025
488620d
Fixed some indentation in AcademicPagesExporterTest
GreLucie Dec 6, 2025
5d6e3a2
add all formatters as class variable
Memeel Dec 6, 2025
91a6a05
Fixed tests not passing.
AloisHasNeurons Dec 7, 2025
6ad9b7a
Update jablib/src/main/java/org/jabref/logic/exporter/AcademicPagesEx…
GreLucie Dec 7, 2025
d9ab1a6
Update jablib/src/main/java/org/jabref/logic/exporter/AcademicPagesEx…
GreLucie Dec 7, 2025
a587653
Update jablib/src/main/java/org/jabref/logic/exporter/AcademicPagesEx…
GreLucie Dec 7, 2025
43576c3
Merge branch 'main' into fix-for-issue-12727
GreLucie Dec 7, 2025
fc3e5bd
Fixed test exportInProceedingsWithBooktitleUsesBooktitleAsVenueAlias …
AloisHasNeurons Dec 8, 2025
47dc573
Changed NumberMonthFormatter.java into MonthNumberFormatter.java
GreLucie Dec 8, 2025
219a5c5
Merge branch 'fix-for-issue-12727' of https://github.com/GreLucie/jab…
GreLucie Dec 8, 2025
3e156fb
Resolved trailing white space in Changelog
GreLucie Dec 8, 2025
305135a
useless import
GreLucie Dec 8, 2025
370b680
Merge branch 'main' into fix-for-issue-12727
GreLucie Dec 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv

### Removed

## [6.0-alpha.3] – 2025-10-30
## [6.0-alpha.3] – 2025-11-29
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert this change


### Added

- We added another custom Exporter AcademicPagesExporter and a new layout as well as the corresponding line in ExporterFactory. [#12727](...)
- We added an initial [cite as you write](https://retorque.re/zotero-better-bibtex/citing/cayw/) ("CAYW") endpoint. [#13187](https://github.com/JabRef/jabref/issues/13187)
- We added a field for the latest ICORE conference ranking lookup on the General Tab. [#13476](https://github.com/JabRef/jabref/issues/13476)
- We added the option to enable the language server in the preferences. [#13697](https://github.com/JabRef/jabref/pull/13697)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package org.jabref.logic.exporter;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

import org.jabref.logic.journals.JournalAbbreviationLoader;
import org.jabref.logic.journals.JournalAbbreviationRepository;
import org.jabref.logic.layout.LayoutFormatterPreferences;
import org.jabref.logic.util.StandardFileType;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.metadata.SelfContainedSaveOrder;

import org.jspecify.annotations.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* A custom exporter to write multiple bib entries as AcademicPages Markdown format.
*/
public class AcademicPagesExporter extends Exporter {
private static final String BLANK_LINE_PATTERN = "\\r\\n|\\n";
private static final String LAYOUT_PREFIX = "/resource/layout/";
private static final String LAYOUT_EXTENSION = ".layout";
private static final String FORMATTERS_EXTENSION = ".formatters";
private static final String BEGIN_INFIX = ".begin";
private static final String END_INFIX = ".end";

private static final Logger LOGGER = LoggerFactory.getLogger(TemplateExporter.class);

private final String lfFileName;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not abbreviate. Use layoutFileFileName or short layoutFile (because you don't have layoutFileContent.

private final String directory;
private final LayoutFormatterPreferences layoutPreferences;
private final SelfContainedSaveOrder saveOrder;
private boolean customExport;
private List<BibEntry> entries;
private final TemplateExporter academicPagesTemplate;

/**
* Initialize another export format based on templates stored in dir with layoutFile lfFilename.
*
*/
Comment on lines 39 to 41
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment is strange. What is lfFilename? Where does it come from? I think, this is not something a user of the class should know. You can just delete whole JavaDoc comment.

public AcademicPagesExporter(LayoutFormatterPreferences layoutPreferences, SelfContainedSaveOrder saveOrder) {
super("academicpages", "academicpages", StandardFileType.MARKDOWN);
this.lfFileName = "academicpages";
this.directory = "academicpages";
this.layoutPreferences = layoutPreferences;
this.saveOrder = saveOrder;
String consoleName = "academicpages";
this.academicPagesTemplate = new TemplateExporter("academicpages", consoleName, lfFileName, directory, StandardFileType.MARKDOWN, layoutPreferences, saveOrder);
}

@Override
public void export(@NonNull final BibDatabaseContext databaseContext,
final Path exportDirectory,
@NonNull List<BibEntry> entries) throws SaveException {
export(databaseContext, exportDirectory, entries, List.of(), JournalAbbreviationLoader.loadBuiltInRepository());
}

/**
* The method that performs the export of all entries by iterating on the entries.
*
* @param databaseContext the database to export from
* @param exportDirectory the directory to write to
* @param entries a list containing all entries that should be exported
* @param abbreviationRepository the built-in repository
* @throws SaveException Exception thrown if saving goes wrong
*/
@Override
public void export(@NonNull final BibDatabaseContext databaseContext,
final Path exportDirectory,
@NonNull List<BibEntry> entries,
List<Path> fileDirForDataBase,
JournalAbbreviationRepository abbreviationRepository) throws SaveException {
if (entries.isEmpty()) { // Only export if entries exist
return;
}
try {
Integer iterator = 1;
for (BibEntry entry : entries) {
Path path = Paths.get(exportDirectory.toString(), iterator.toString());
iterator += 1;
academicPagesTemplate.export(databaseContext, path, entries, fileDirForDataBase, abbreviationRepository);
}
} catch (IOException e) {
return;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.jabref.logic.util.StandardFileType;
import org.jabref.logic.xmp.XmpPreferences;
import org.jabref.model.database.BibDatabaseMode;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.metadata.SelfContainedSaveOrder;

import org.jspecify.annotations.NonNull;
Expand Down Expand Up @@ -54,6 +55,7 @@ public static ExporterFactory create(CliPreferences preferences) {
exporters.add(new TemplateExporter("MIS Quarterly", "misq", "misq", "misq", StandardFileType.RTF, layoutPreferences, saveOrder));
exporters.add(new TemplateExporter("CSL YAML", "yaml", "yaml", null, StandardFileType.YAML, layoutPreferences, saveOrder, BlankLineBehaviour.DELETE_BLANKS));
exporters.add(new TemplateExporter("Hayagriva YAML", "hayagrivayaml", "hayagrivayaml", null, StandardFileType.YAML, layoutPreferences, saveOrder, BlankLineBehaviour.DELETE_BLANKS));
exporters.add(new TemplateExporter(Localization.lang("Markdown academicpages"), "academicpages", "academicpages", "academicpages", StandardFileType.MARKDOWN, layoutPreferences, saveOrder));
exporters.add(new OpenOfficeDocumentCreator());
exporters.add(new OpenDocumentSpreadsheetCreator());
exporters.add(new MSBibExporter());
Expand All @@ -63,6 +65,7 @@ public static ExporterFactory create(CliPreferences preferences) {
exporters.add(new EmbeddedBibFilePdfExporter(bibDatabaseMode, preferences.getCustomEntryTypesRepository(), fieldPreferences));
exporters.add(new CffExporter());
exporters.add(new EndnoteXmlExporter(preferences.getBibEntryPreferences()));
exporters.add(new AcademicPagesExporter(layoutPreferences, saveOrder));

// Now add custom export formats
exporters.addAll(customFormats);
Expand Down
3 changes: 3 additions & 0 deletions jablib/src/main/java/org/jabref/logic/layout/LayoutEntry.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
import org.jabref.logic.layout.format.NonSpaceWhitespaceRemover;
import org.jabref.logic.layout.format.NotFoundFormatter;
import org.jabref.logic.layout.format.Number;
import org.jabref.logic.layout.format.NumberMonthFormatter;
import org.jabref.logic.layout.format.Ordinal;
import org.jabref.logic.layout.format.RTFChars;
import org.jabref.logic.layout.format.RemoveBrackets;
Expand Down Expand Up @@ -565,6 +566,8 @@ private LayoutFormatter getLayoutFormatterByName(String name) {
new ReplaceWithEscapedDoubleQuotes();
case "HayagrivaType" ->
new HayagrivaType();
case "NumberMonth" ->
new NumberMonthFormatter();
default ->
null;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.jabref.logic.layout.format;

import java.util.Optional;

import org.jabref.logic.layout.LayoutFormatter;
import org.jabref.model.entry.Month;

public class NumberMonthFormatter implements LayoutFormatter {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, it should be called MonthNumberFormatter


@Override
public String format(String fieldText) {
Optional<Month> month = Month.parse(fieldText);
return month.map(Month::getTwoDigitNumber).orElse("01");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merge this into one line plese.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we adressed all of the requested changes in our last commit. More tests were also added by one of our team members.

}
}
1 change: 1 addition & 0 deletions jablib/src/main/resources/l10n/JabRef_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ The\ marked\ area\ does\ not\ contain\ any\ legible\ text!=The marked area does
HTML\ table=HTML table
HTML\ table\ (with\ Abstract\ &\ BibTeX)=HTML table (with Abstract & BibTeX)
Markdown\ titles=Markdown titles
Markdown\ academicpages=Academic Pages Markdowns

Icon=Icon

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
title: "\format[RemoveLatexCommands,HTMLChars]{\title}"
collection: publications
category: \format{\entrytype}
permalink: /publication/\format{\year}-\begin{month}\format{\month}\end{month}\begin{!month}01\end{!month}-\begin{day}\format{\day}\end{day}\begin{!day}01\end{!day}-\format[RemoveLatexCommands,HTMLChars,Replace(\s,-)]{\title}
excerpt: '\begin{note}\format[RemoveLatexCommands,HTMLChars]{\note}\end{note}'
date: \format{\year}-\begin{month}\format[NumberMonth]{\month}\end{month}\begin{!month}01\end{!month}-\begin{day}\format{\day}\end{day}\begin{!day}01\end{!day}
venue: '\format[RemoveLatexCommands,HTMLChars]{\journal}\begin{!journal}Unknown\end{!journal}'
slidesurl: '\begin{file}\format[FileLink(pdf)]{\file}\end{file}\begin{!file}https://[insert username].github.io/files/[insert filename].pdf\end{!file}'
paperurl: '\begin{file}\format[FileLink(pdf)]{\file}\end{file}\begin{!file}https://[insert username].github.io/files/[insert filename].pdf\end{!file}'
bibtexurl: 'https://[insert username].github.io/files/[insert filename].bib'
citation: '\format[HTMLChars]{\author}. (\format{\year}). "&quot;\format[RemoveLatexCommands,HTMLChars]{\title}.&quot; <i>\format[RemoveLatexCommands,HTMLChars]{\journal}</i>.'
---
\begin{abstract}\format{\abstract}\end{abstract}
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package org.jabref.logic.exporter;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

import org.jabref.logic.layout.LayoutFormatterPreferences;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.field.StandardField;
import org.jabref.model.entry.types.StandardEntryType;
import org.jabref.model.metadata.SaveOrder;
import org.jabref.model.metadata.SelfContainedSaveOrder;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.Answers;

import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;

class AcademicPagesExporterTest {

private AcademicPagesExporter exporter;
private BibDatabaseContext databaseContext;

@BeforeEach
void setUp() {
exporter = new AcademicPagesExporter(
mock(LayoutFormatterPreferences.class, Answers.RETURNS_DEEP_STUBS),
new SelfContainedSaveOrder(SaveOrder.OrderType.SPECIFIED, List.of()));
databaseContext = new BibDatabaseContext();
}

@Test
void exportArticleWithFullDateAndRequiredFieldsGeneratesCorrectFileNameAndContent(@TempDir Path tempDir) throws Exception {
BibEntry entry = new BibEntry(StandardEntryType.Article)
.withCitationKey("testKey")
.withField(StandardField.TITLE, "Test Title")
.withField(StandardField.AUTHOR, "Test Author")
.withField(StandardField.YEAR, "2023")
.withField(StandardField.MONTH, "05")
.withField(StandardField.DAY, "12")
.withField(StandardField.JOURNAL, "Test Journal");

exporter.export(databaseContext, tempDir, List.of(entry));

// Verify file name follows pattern: YYYY-MM-DD-title.md
Path expectedFile = tempDir.resolve("2023-05-12-test-title.md");
assertTrue(Files.exists(expectedFile));

String content = Files.readString(expectedFile);

// Verify YAML front matter fields
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please follow the pattern of other tests in that directory, do a full comparison. of the content

assertTrue(content.contains("title: \"Test Title\""));
assertTrue(content.contains("date: 2023-05-12"));
assertTrue(content.contains("venue: 'Test Journal'"));
assertTrue(content.contains("citation: 'Test Author (2023). \"Test Title.\" <i>Test Journal</i>.'"));
}

@Test
void exportArticleWithMissingMonthAndDayDefaultsToJanuaryFirst(@TempDir Path tempDir) throws Exception {
BibEntry entry = new BibEntry(StandardEntryType.Article)
.withCitationKey("testKey")
.withField(StandardField.TITLE, "No Date")
.withField(StandardField.YEAR, "2023");

exporter.export(databaseContext, tempDir, List.of(entry));

// Expect default date 2023-01-01
Path expectedFile = tempDir.resolve("2023-01-01-no-date.md");
assertTrue(Files.exists(expectedFile));

String content = Files.readString(expectedFile);
assertTrue(content.contains("date: 2023-01-01"));
}

@Test
void exportArticleWithAbstractAppendsAbstractAfterYamlFrontMatter(@TempDir Path tempDir) throws Exception {
BibEntry entry = new BibEntry(StandardEntryType.Article)
.withCitationKey("testKey")
.withField(StandardField.TITLE, "Abstract Paper")
.withField(StandardField.YEAR, "2023")
.withField(StandardField.ABSTRACT, "This is a test abstract.");

exporter.export(databaseContext, tempDir, List.of(entry));

Path expectedFile = tempDir.resolve("2023-01-01-abstract-paper.md");
assertTrue(Files.exists(expectedFile));

String content = Files.readString(expectedFile);

// Abstract should be outside the YAML block (after the second '---')
assertTrue(content.contains("---"));
assertTrue(content.endsWith("\nThis is a test abstract.\n"));
}

@Test
void exportMultipleEntriesGeneratesMultipleIndividualMarkdownFiles(@TempDir Path tempDir) throws Exception {
BibEntry entry1 = new BibEntry(StandardEntryType.Article)
.withCitationKey("key1")
.withField(StandardField.TITLE, "Paper One")
.withField(StandardField.YEAR, "2023");

BibEntry entry2 = new BibEntry(StandardEntryType.Book)
.withCitationKey("key2")
.withField(StandardField.TITLE, "Book Two")
.withField(StandardField.YEAR, "2022");

exporter.export(databaseContext, tempDir, List.of(entry1, entry2));

// Verify both files exist
assertTrue(Files.exists(tempDir.resolve("2023-01-01-paper-one.md")));
assertTrue(Files.exists(tempDir.resolve("2022-01-01-book-two.md")));
}

@Test
void exportInProceedingsWithBooktitleUsesBooktitleAsVenueAlias(@TempDir Path tempDir) throws Exception {
BibEntry entry = new BibEntry(StandardEntryType.InProceedings)
.withCitationKey("testKey")
.withField(StandardField.TITLE, "Conference Paper")
.withField(StandardField.YEAR, "2023")
.withField(StandardField.BOOKTITLE, "Conference Proceedings");

exporter.export(databaseContext, tempDir, List.of(entry));

Path expectedFile = tempDir.resolve("2023-01-01-conference-paper.md");
assertTrue(Files.exists(expectedFile));

String content = Files.readString(expectedFile);

// 'venue' should be populated from 'booktitle' since 'journal' is missing
assertTrue(content.contains("venue: 'Conference Proceedings'"));
}
}
Loading