[office/tellico] /: Check title field existence before exporting entry HTML

Robby Stephenson <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit 69e223ad6fe7aa2b0f92a37f903ebd446ec8c8b8 by Robby Stephenson.
Committed on 01/08/2026 at 00:48.
Pushed by rstephenson into branch 'master'.

Check title field existence before exporting entry HTML

Fix crash for entries with no fields at all or a non-title field. Use
"entry" in the exported file name if there is an empty title.

BUG: 523140
FIXED-IN: 4.2.2

M  +4    -0    ChangeLog
M  +48   -0    src/tests/htmlexportertest.cpp
M  +2    -0    src/tests/htmlexportertest.h
M  +8    -2    src/translators/htmlexporter.cpp

https://invent.kde.org/office/tellico/-/commit/69e223ad6fe7aa2b0f92a37f903ebd446ec8c8b8

diff --git a/ChangeLog b/ChangeLog
index f730554ef..d4b4df85c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2026-07-26  Robby Stephenson  <[email protected]>
+
+	* Fixed crashing bug when exporting HTML for entries with no title (Bug 523140).
+
 2026-07-05  Robby Stephenson  <[email protected]>
 
 	* Added drag-and-drop text importing for RIS.
diff --git a/src/tests/htmlexportertest.cpp b/src/tests/htmlexportertest.cpp
index 6f766a082..6748c0466 100644
--- a/src/tests/htmlexportertest.cpp
+++ b/src/tests/htmlexportertest.cpp
@@ -474,3 +474,51 @@ void HtmlExporterTest::testLinkedImage() {
   QVERIFY2(Tellico::GUI::Proxy::lastSorry().isEmpty(),
            "Last sorry message is not empty after writing output");
 }
+
+// https://bugs.kde.org/show_bug.cgi?id=523140
+void HtmlExporterTest::testNoFields() {
+  // no fields at all
+  Tellico::Data::CollPtr coll(new Tellico::Data::Collection(false));
+  Tellico::Data::EntryPtr e(new Tellico::Data::Entry(coll));
+  coll->addEntries(e);
+
+  QTemporaryDir tempDir;
+  QVERIFY(tempDir.isValid());
+  tempDir.setAutoRemove(true);
+
+  Tellico::Export::HTMLExporter exporter(coll, QUrl());
+  exporter.setURL(QUrl::fromLocalFile(tempDir.path() + "/testHtml.html"));
+  exporter.setXSLTFile(QFINDTESTDATA("../../xslt/tellico2html.xsl"));
+  exporter.setParseDOM(true); // required since HtmlExporter only creates individual entry files if parsing DOM
+  exporter.setEntries(coll->entries());
+  exporter.setExportEntryFiles(true);
+  exporter.setEntryXSLTFile(QStringLiteral("Fancy"));
+
+  QVERIFY(exporter.exec());
+}
+
+// https://bugs.kde.org/show_bug.cgi?id=523140
+void HtmlExporterTest::testNoTitle() {
+  // no explicit title field
+  Tellico::Data::CollPtr coll(new Tellico::Data::Collection(false));
+  Tellico::Data::FieldPtr f(new Tellico::Data::Field(QStringLiteral("name"), QStringLiteral("Name")));
+  coll->addField(f);
+
+  Tellico::Data::EntryPtr e(new Tellico::Data::Entry(coll));
+  e->setField(f->name(), QStringLiteral("test"));
+  coll->addEntries(e);
+
+  QTemporaryDir tempDir;
+  QVERIFY(tempDir.isValid());
+  tempDir.setAutoRemove(true);
+
+  Tellico::Export::HTMLExporter exporter(coll, QUrl());
+  exporter.setURL(QUrl::fromLocalFile(tempDir.path() + "/testHtml.html"));
+  exporter.setXSLTFile(QFINDTESTDATA("../../xslt/tellico2html.xsl"));
+  exporter.setParseDOM(true); // required since HtmlExporter only creates individual entry files if parsing DOM
+  exporter.setEntries(coll->entries());
+  exporter.setExportEntryFiles(true);
+  exporter.setEntryXSLTFile(QStringLiteral("Fancy"));
+
+  QVERIFY(exporter.exec());
+}
diff --git a/src/tests/htmlexportertest.h b/src/tests/htmlexportertest.h
index 6f8cd9374..f5b09a8c9 100644
--- a/src/tests/htmlexportertest.h
+++ b/src/tests/htmlexportertest.h
@@ -44,6 +44,8 @@ private Q_SLOTS:
   void testEntryTemplates_data();
   void testPrinting();
   void testLinkedImage();
+  void testNoFields();
+  void testNoTitle();
 };
 
 #endif
diff --git a/src/translators/htmlexporter.cpp b/src/translators/htmlexporter.cpp
index daf9fe982..ac3237bda 100644
--- a/src/translators/htmlexporter.cpp
+++ b/src/translators/htmlexporter.cpp
@@ -141,6 +141,9 @@ void HTMLExporter::setParseDOM(bool parseDOM) {
 
 void HTMLExporter::setExportEntryFiles(bool exportEntryFiles) {
   m_exportEntryFiles = exportEntryFiles;
+  if(m_exportEntryFiles && !m_parseDOM) {
+    myWarning() << "HTMLExporter will not export entry files when DOM is not parsed";
+  }
 }
 
 void HTMLExporter::setCustomHtml(const QString& html_) {
@@ -821,12 +824,15 @@ bool HTMLExporter::writeEntryFiles() {
   exporter.setCollectionURL(url());
   bool parseDOM = true;
 
-  const QString title = QStringLiteral("title");
+  const QString title = collection()->titleField();
   const QString html = QStringLiteral(".html");
-  bool multipleTitles = collection()->fieldByName(title)->hasFlag(Data::Field::AllowMultiple);
+  const bool multipleTitles = !title.isEmpty() && collection()->fieldByName(title)->hasFlag(Data::Field::AllowMultiple);
   Data::EntryList entries = this->entries(); // not const since the pointer has to be copied
   foreach(Data::EntryPtr entryIt, entries) {
     QString file = entryIt->title(formatted);
+    if(file.isEmpty()) {
+      file = QStringLiteral("entry");
+    }
 
     // but only use the first title if it has multiple
     if(multipleTitles) {
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.